Merge pull request 'national_admin_functions' (#1) from national_admin_functions into master
This commit is contained in:
commit
c96e71103a
|
@ -32,7 +32,7 @@ public class MainApplication {
|
||||||
return new Docket(DocumentationType.SWAGGER_2)
|
return new Docket(DocumentationType.SWAGGER_2)
|
||||||
.select()
|
.select()
|
||||||
.apis(RequestHandlerSelectors.any())
|
.apis(RequestHandlerSelectors.any())
|
||||||
.paths(p -> p.startsWith("/api/"))
|
.paths(p -> p.startsWith("/api/") || p.startsWith("/oa_api"))
|
||||||
.build()
|
.build()
|
||||||
.apiInfo(new ApiInfoBuilder()
|
.apiInfo(new ApiInfoBuilder()
|
||||||
.title("D-Net Organizations Service APIs")
|
.title("D-Net Organizations Service APIs")
|
||||||
|
|
|
@ -23,12 +23,12 @@ public class MyAccessDeniedHandler implements AccessDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(final HttpServletRequest req, final HttpServletResponse res, final AccessDeniedException e)
|
public void handle(final HttpServletRequest req, final HttpServletResponse res, final AccessDeniedException e)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
|
|
||||||
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
if (auth != null) {
|
if (auth != null) {
|
||||||
logger.warn(String.format("User '%s' attempted to access the protected URL: %s", auth.getName(), req.getRequestURI()));
|
logger.warn(String.format("User '%s' (%s) attempted to access the protected URL: %s", auth.getName(), req.getRemoteAddr(), req.getRequestURI()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserInfo.isNotAuthorized(auth)) {
|
if (UserInfo.isNotAuthorized(auth)) {
|
||||||
|
|
|
@ -33,7 +33,8 @@ public class OpenaireInternalApiController {
|
||||||
log.warn("Call received by blaklisted ip (https proxy): " + req.getRemoteAddr());
|
log.warn("Call received by blaklisted ip (https proxy): " + req.getRemoteAddr());
|
||||||
throw new RuntimeException("Call received by blaklisted ip (https proxy): " + req.getRemoteAddr());
|
throw new RuntimeException("Call received by blaklisted ip (https proxy): " + req.getRemoteAddr());
|
||||||
}
|
}
|
||||||
new Thread(databaseUtils::importDedupEvents).run();
|
new Thread(databaseUtils::importDedupEvents).start();
|
||||||
|
|
||||||
return Arrays.asList("Importing simrels (request from " + req.getRemoteAddr() + ") ...");
|
return Arrays.asList("Importing simrels (request from " + req.getRemoteAddr() + ") ...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -26,7 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import eu.dnetlib.organizations.model.OpenaireDuplicate;
|
import eu.dnetlib.organizations.model.OpenaireDuplicate;
|
||||||
import eu.dnetlib.organizations.model.Relationship;
|
import eu.dnetlib.organizations.model.Organization;
|
||||||
import eu.dnetlib.organizations.model.utils.BrowseEntry;
|
import eu.dnetlib.organizations.model.utils.BrowseEntry;
|
||||||
import eu.dnetlib.organizations.model.utils.OrganizationConflict;
|
import eu.dnetlib.organizations.model.utils.OrganizationConflict;
|
||||||
import eu.dnetlib.organizations.model.view.ConflictGroupView;
|
import eu.dnetlib.organizations.model.view.ConflictGroupView;
|
||||||
|
@ -35,6 +34,7 @@ import eu.dnetlib.organizations.model.view.OpenaireDuplicateView;
|
||||||
import eu.dnetlib.organizations.model.view.OrganizationInfoView;
|
import eu.dnetlib.organizations.model.view.OrganizationInfoView;
|
||||||
import eu.dnetlib.organizations.model.view.OrganizationSimpleView;
|
import eu.dnetlib.organizations.model.view.OrganizationSimpleView;
|
||||||
import eu.dnetlib.organizations.model.view.OrganizationView;
|
import eu.dnetlib.organizations.model.view.OrganizationView;
|
||||||
|
import eu.dnetlib.organizations.repository.OrganizationRepository;
|
||||||
import eu.dnetlib.organizations.repository.UserCountryRepository;
|
import eu.dnetlib.organizations.repository.UserCountryRepository;
|
||||||
import eu.dnetlib.organizations.repository.readonly.ConflictGroupViewRepository;
|
import eu.dnetlib.organizations.repository.readonly.ConflictGroupViewRepository;
|
||||||
import eu.dnetlib.organizations.repository.readonly.DuplicateGroupViewRepository;
|
import eu.dnetlib.organizations.repository.readonly.DuplicateGroupViewRepository;
|
||||||
|
@ -49,6 +49,8 @@ import eu.dnetlib.organizations.utils.DatabaseUtils;
|
||||||
@RequestMapping("/api/organizations")
|
@RequestMapping("/api/organizations")
|
||||||
public class OrganizationController {
|
public class OrganizationController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrganizationRepository organizationRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OrganizationViewRepository organizationViewRepository;
|
private OrganizationViewRepository organizationViewRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -78,7 +80,7 @@ public class OrganizationController {
|
||||||
} else if (StringUtils.isBlank(org.getType())) {
|
} else if (StringUtils.isBlank(org.getType())) {
|
||||||
throw new RuntimeException("Missing field: type");
|
throw new RuntimeException("Missing field: type");
|
||||||
} else if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) {
|
} else if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) {
|
||||||
final String orgId = databaseUtils.insertOrUpdateOrganization(org, authentication.getName());
|
final String orgId = databaseUtils.insertOrUpdateOrganization(org, authentication.getName(), UserInfo.isSimpleUser(authentication));
|
||||||
return Arrays.asList(orgId);
|
return Arrays.asList(orgId);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("User not authorized");
|
throw new RuntimeException("User not authorized");
|
||||||
|
@ -118,6 +120,30 @@ public class OrganizationController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/delete")
|
||||||
|
public OrganizationView deleteById(@RequestParam final String id, final Authentication authentication) {
|
||||||
|
final Organization org = organizationRepository.findById(id).get();
|
||||||
|
|
||||||
|
if (UserInfo.isSuperAdmin(authentication) || UserInfo.isNationalAdmin(authentication) &&
|
||||||
|
userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) {
|
||||||
|
return databaseUtils.markAsDeleted(id, authentication.getName());
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("User not authorized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/discard")
|
||||||
|
public OrganizationView discardById(@RequestParam final String id, final Authentication authentication) {
|
||||||
|
final Organization org = organizationRepository.findById(id).get();
|
||||||
|
|
||||||
|
if (UserInfo.isSuperAdmin(authentication) || UserInfo.isNationalAdmin(authentication) &&
|
||||||
|
userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) {
|
||||||
|
return databaseUtils.markAsDiscarded(id, authentication.getName());
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("User not authorized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/conflicts")
|
@GetMapping("/conflicts")
|
||||||
public List<OrganizationConflict> conflicts(@RequestParam final String id, final Authentication authentication) {
|
public List<OrganizationConflict> conflicts(@RequestParam final String id, final Authentication authentication) {
|
||||||
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, authentication.getName())) {
|
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, authentication.getName())) {
|
||||||
|
@ -139,7 +165,7 @@ public class OrganizationController {
|
||||||
@GetMapping("/conflicts/byCountry/{country}")
|
@GetMapping("/conflicts/byCountry/{country}")
|
||||||
public Collection<Set<OrganizationConflict>> findConflictsByCountry(@PathVariable final String country, final Authentication authentication) {
|
public Collection<Set<OrganizationConflict>> findConflictsByCountry(@PathVariable final String country, final Authentication authentication) {
|
||||||
|
|
||||||
databaseUtils.verifyConflictGroups(false);
|
// databaseUtils.verifyConflictGroups(false);
|
||||||
|
|
||||||
if (UserInfo.isSuperAdmin(authentication)) {
|
if (UserInfo.isSuperAdmin(authentication)) {
|
||||||
return groupConflicts(conflictGroupViewRepository.findByCountry1OrCountry2(country, country).stream());
|
return groupConflicts(conflictGroupViewRepository.findByCountry1OrCountry2(country, country).stream());
|
||||||
|
@ -210,26 +236,33 @@ public class OrganizationController {
|
||||||
public Page<OrganizationSimpleView> search(@PathVariable final int page,
|
public Page<OrganizationSimpleView> search(@PathVariable final int page,
|
||||||
@PathVariable final int size,
|
@PathVariable final int size,
|
||||||
@RequestParam final String q,
|
@RequestParam final String q,
|
||||||
|
@RequestParam(required = false, defaultValue = "suggested,approved") final String status,
|
||||||
final Authentication authentication) {
|
final Authentication authentication) {
|
||||||
|
|
||||||
return UserInfo.isSuperAdmin(authentication)
|
return UserInfo.isSuperAdmin(authentication)
|
||||||
? organizationSimpleViewRepository.search(q, PageRequest.of(page, size))
|
? organizationSimpleViewRepository.search(q, Arrays.asList(status.split(",")), PageRequest.of(page, size))
|
||||||
: organizationSimpleViewRepository.searchForUser(q, authentication.getName(), PageRequest.of(page, size));
|
: organizationSimpleViewRepository.searchForUser(q, authentication.getName(), Arrays.asList(status.split(",")), PageRequest.of(page, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/byCountry/{code}/{page}/{size}")
|
@GetMapping("/byCountry/{status}/{code}/{page}/{size}")
|
||||||
public Page<OrganizationSimpleView> findByCountry(@PathVariable final String code,
|
public Page<OrganizationSimpleView> findByCountry(@PathVariable final String status,
|
||||||
|
@PathVariable final String code,
|
||||||
@PathVariable final int page,
|
@PathVariable final int page,
|
||||||
@PathVariable final int size,
|
@PathVariable final int size,
|
||||||
final Authentication authentication) {
|
final Authentication authentication) {
|
||||||
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) {
|
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) {
|
||||||
return organizationSimpleViewRepository.findByCountryOrderByName(code, PageRequest.of(page, size));
|
if (status.equalsIgnoreCase("all")) {
|
||||||
|
return organizationSimpleViewRepository.findByCountryOrderByName(code, PageRequest.of(page, size));
|
||||||
|
} else {
|
||||||
|
return organizationSimpleViewRepository.findByCountryAndStatusOrderByName(code, status, PageRequest.of(page, size));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("User not authorized");
|
throw new RuntimeException("User not authorized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/byCountry/{status}/{code}")
|
@GetMapping("/byCountry/{status}/{code}")
|
||||||
public Iterable<OrganizationSimpleView> findPendingOrgsByCountry(@PathVariable final String status,
|
public Iterable<OrganizationSimpleView> findOrgsByStatusAndCountry(@PathVariable final String status,
|
||||||
@PathVariable final String code,
|
@PathVariable final String code,
|
||||||
final Authentication authentication) {
|
final Authentication authentication) {
|
||||||
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) {
|
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) {
|
||||||
|
@ -243,14 +276,27 @@ public class OrganizationController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/byType/{type}/{page}/{size}")
|
@GetMapping("/byType/{status}/{type}/{page}/{size}")
|
||||||
public Page<OrganizationSimpleView> findByType(@PathVariable final String type,
|
public Page<OrganizationSimpleView> findByType(@PathVariable final String status,
|
||||||
|
@PathVariable final String type,
|
||||||
@PathVariable final int page,
|
@PathVariable final int page,
|
||||||
@PathVariable final int size,
|
@PathVariable final int size,
|
||||||
final Authentication authentication) {
|
final Authentication authentication) {
|
||||||
return UserInfo.isSuperAdmin(authentication)
|
|
||||||
? organizationSimpleViewRepository.findByTypeOrderByName(type, PageRequest.of(page, size))
|
if (UserInfo.isSuperAdmin(authentication)) {
|
||||||
: organizationSimpleViewRepository.findByTypeForUser(type, authentication.getName(), PageRequest.of(page, size));
|
if (status.equalsIgnoreCase("all")) {
|
||||||
|
return organizationSimpleViewRepository.findByTypeOrderByName(type, PageRequest.of(page, size));
|
||||||
|
} else {
|
||||||
|
return organizationSimpleViewRepository.findByTypeAndStatusOrderByName(type, status, PageRequest.of(page, size));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (status.equalsIgnoreCase("all")) {
|
||||||
|
return organizationSimpleViewRepository.findByTypeForUser(type, authentication.getName(), PageRequest.of(page, size));
|
||||||
|
} else {
|
||||||
|
return organizationSimpleViewRepository.findByTypeAndStatusForUser(type, status, authentication.getName(), PageRequest.of(page, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/browse/countries")
|
@GetMapping("/browse/countries")
|
||||||
|
@ -268,18 +314,17 @@ public class OrganizationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/conflicts/fix/{masterId}")
|
@PostMapping("/conflicts/fix/{masterId}")
|
||||||
public List<Relationship> fixConflicts(final Authentication authentication, @PathVariable final String masterId, @RequestBody final List<String> otherIds) {
|
public List<String> fixConflicts(final Authentication authentication, @PathVariable final String masterId, @RequestBody final List<String> otherIds) {
|
||||||
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(masterId, authentication.getName())) {
|
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(masterId, authentication.getName())) {
|
||||||
|
final List<String> list = new ArrayList<>();
|
||||||
|
list.add(masterId);
|
||||||
|
list.addAll(otherIds);
|
||||||
|
final String newOrgId = databaseUtils.fixConflict(list, authentication.getName());
|
||||||
|
|
||||||
return otherIds.stream()
|
return Arrays.asList(newOrgId);
|
||||||
.filter(id -> UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, authentication.getName()))
|
|
||||||
.map(id -> databaseUtils.fixDuplicate(masterId, id))
|
|
||||||
.flatMap(List::stream)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
} else {
|
} else {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,14 @@ public class OpenaireDuplicate implements Serializable {
|
||||||
@Column(name = "reltype")
|
@Column(name = "reltype")
|
||||||
private String relType;
|
private String relType;
|
||||||
|
|
||||||
|
public OpenaireDuplicate() {}
|
||||||
|
|
||||||
|
public OpenaireDuplicate(final String localId, final String oaOriginalId, final String relType) {
|
||||||
|
this.localId = localId;
|
||||||
|
this.oaOriginalId = oaOriginalId;
|
||||||
|
this.relType = relType;
|
||||||
|
}
|
||||||
|
|
||||||
public String getLocalId() {
|
public String getLocalId() {
|
||||||
return localId;
|
return localId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package eu.dnetlib.organizations.model.utils;
|
package eu.dnetlib.organizations.model.utils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class BrowseEntry implements Serializable {
|
public class BrowseEntry implements Serializable {
|
||||||
|
|
||||||
|
@ -9,17 +11,16 @@ public class BrowseEntry implements Serializable {
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = 8854955977257064470L;
|
private static final long serialVersionUID = 8854955977257064470L;
|
||||||
|
|
||||||
private String value;
|
private String code;
|
||||||
private String name;
|
private String name;
|
||||||
private int approved;
|
private Map<String, Integer> values = new LinkedHashMap<>();
|
||||||
private int pending;
|
|
||||||
|
|
||||||
public String getValue() {
|
public String getCode() {
|
||||||
return value;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(final String value) {
|
public void setCode(final String code) {
|
||||||
this.value = value;
|
this.code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -30,20 +31,12 @@ public class BrowseEntry implements Serializable {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getApproved() {
|
public Map<String, Integer> getValues() {
|
||||||
return approved;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApproved(final int approved) {
|
public void setValues(final Map<String, Integer> values) {
|
||||||
this.approved = approved;
|
this.values = values;
|
||||||
}
|
|
||||||
|
|
||||||
public int getPending() {
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPending(final int pending) {
|
|
||||||
this.pending = pending;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package eu.dnetlib.organizations.model.utils;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class TempBrowseEntry implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -2233409680550512318L;
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String group;
|
||||||
|
private Integer count;
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(final String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroup(final String group) {
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCount(final Integer count) {
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,6 +46,10 @@ public class OrganizationSimpleView implements Serializable, Comparable<Organiza
|
||||||
@Column(name = "acronyms", columnDefinition = "text[]")
|
@Column(name = "acronyms", columnDefinition = "text[]")
|
||||||
private String[] acronyms;
|
private String[] acronyms;
|
||||||
|
|
||||||
|
@Type(type = "string-array")
|
||||||
|
@Column(name = "urls", columnDefinition = "text[]")
|
||||||
|
private String[] urls;
|
||||||
|
|
||||||
@Column(name = "status")
|
@Column(name = "status")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@ -122,6 +126,14 @@ public class OrganizationSimpleView implements Serializable, Comparable<Organiza
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getUrls() {
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrls(final String[] urls) {
|
||||||
|
this.urls = urls;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id);
|
return Objects.hash(id);
|
||||||
|
|
|
@ -14,6 +14,13 @@ public class OtherName implements Serializable {
|
||||||
|
|
||||||
private String lang;
|
private String lang;
|
||||||
|
|
||||||
|
public OtherName() {}
|
||||||
|
|
||||||
|
public OtherName(final String name, final String lang) {
|
||||||
|
this.name = name;
|
||||||
|
this.lang = lang;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ public interface OpenaireConflictRepository extends JpaRepository<OpenaireConfli
|
||||||
void resetGroupIds();
|
void resetGroupIds();
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Query(value = "update oa_conflicts set modified_by = ?3, modification_date = ?4 where (id1 = ?1 and id2 = ?2) or (id1 = ?2 and id2 = ?1)", nativeQuery = true)
|
@Query(value = "update oa_conflicts set reltype = ?3, modified_by = ?4, modification_date = ?5 where (id1 = ?1 and id2 = ?2) or (id1 = ?2 and id2 = ?1)", nativeQuery = true)
|
||||||
void updateModificationDate(String id1, String id2, String user, OffsetDateTime now);
|
void updateStatus(String id1, String id2, String status, String user, OffsetDateTime now);
|
||||||
|
|
||||||
long countByGroupNull();
|
long countByGroupNull();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package eu.dnetlib.organizations.repository.readonly;
|
package eu.dnetlib.organizations.repository.readonly;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
@ -12,12 +14,15 @@ import eu.dnetlib.organizations.model.view.OrganizationSimpleView;
|
||||||
public interface OrganizationSimpleViewRepository extends ReadOnlyRepository<OrganizationSimpleView, String> {
|
public interface OrganizationSimpleViewRepository extends ReadOnlyRepository<OrganizationSimpleView, String> {
|
||||||
|
|
||||||
// SEARCH
|
// SEARCH
|
||||||
@Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) where idx.txt @@ plainto_tsquery(:text) order by o.name", nativeQuery = true)
|
@Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) where idx.txt @@ plainto_tsquery(:text) and o.status in :statuses order by o.name", nativeQuery = true)
|
||||||
Page<OrganizationSimpleView> search(@Param("text") String text, Pageable pageable);
|
Page<OrganizationSimpleView> search(@Param("text") String text, @Param("statuses") List<String> statuses, Pageable pageable);
|
||||||
|
|
||||||
// SEARCH FOR USER
|
// SEARCH FOR USER
|
||||||
@Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) left outer join user_countries uc on (uc.country = o.country) where idx.txt @@ plainto_tsquery(:text) and uc.email = :email order by o.name", nativeQuery = true)
|
@Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) left outer join user_countries uc on (uc.country = o.country) where idx.txt @@ plainto_tsquery(:text) and uc.email = :email and o.status in :statuses order by o.name", nativeQuery = true)
|
||||||
Page<OrganizationSimpleView> searchForUser(@Param("text") String text, @Param("email") String email, Pageable pageable);
|
Page<OrganizationSimpleView> searchForUser(@Param("text") String text,
|
||||||
|
@Param("email") String email,
|
||||||
|
@Param("statuses") List<String> statuses,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
Page<OrganizationSimpleView> findByCountryOrderByName(String country, Pageable pageable);
|
Page<OrganizationSimpleView> findByCountryOrderByName(String country, Pageable pageable);
|
||||||
|
|
||||||
|
@ -25,9 +30,16 @@ public interface OrganizationSimpleViewRepository extends ReadOnlyRepository<Org
|
||||||
|
|
||||||
Iterable<OrganizationSimpleView> findByCountryAndStatusOrderByName(String code, String status);
|
Iterable<OrganizationSimpleView> findByCountryAndStatusOrderByName(String code, String status);
|
||||||
|
|
||||||
|
Page<OrganizationSimpleView> findByCountryAndStatusOrderByName(String code, String status, Pageable pageable);
|
||||||
|
|
||||||
Page<OrganizationSimpleView> findByTypeOrderByName(String type, Pageable pageable);
|
Page<OrganizationSimpleView> findByTypeOrderByName(String type, Pageable pageable);
|
||||||
|
|
||||||
|
Page<OrganizationSimpleView> findByTypeAndStatusOrderByName(String type, String status, Pageable pageable);
|
||||||
|
|
||||||
@Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where uc.email = ?2 and o.type = ?1 order by o.name", nativeQuery = true)
|
@Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where uc.email = ?2 and o.type = ?1 order by o.name", nativeQuery = true)
|
||||||
Page<OrganizationSimpleView> findByTypeForUser(String type, String name, Pageable pageable);
|
Page<OrganizationSimpleView> findByTypeForUser(String type, String name, Pageable pageable);
|
||||||
|
|
||||||
|
@Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where o.type = ?1 and o.status = ?2 and uc.email = ?3 order by o.name", nativeQuery = true)
|
||||||
|
Page<OrganizationSimpleView> findByTypeAndStatusForUser(String type, String status, String name, Pageable pageable);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package eu.dnetlib.organizations.utils;
|
package eu.dnetlib.organizations.utils;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.function.Function;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -38,6 +40,7 @@ import eu.dnetlib.organizations.model.User;
|
||||||
import eu.dnetlib.organizations.model.UserCountry;
|
import eu.dnetlib.organizations.model.UserCountry;
|
||||||
import eu.dnetlib.organizations.model.utils.BrowseEntry;
|
import eu.dnetlib.organizations.model.utils.BrowseEntry;
|
||||||
import eu.dnetlib.organizations.model.utils.OrganizationConflict;
|
import eu.dnetlib.organizations.model.utils.OrganizationConflict;
|
||||||
|
import eu.dnetlib.organizations.model.utils.TempBrowseEntry;
|
||||||
import eu.dnetlib.organizations.model.utils.VocabularyTerm;
|
import eu.dnetlib.organizations.model.utils.VocabularyTerm;
|
||||||
import eu.dnetlib.organizations.model.view.OrganizationView;
|
import eu.dnetlib.organizations.model.view.OrganizationView;
|
||||||
import eu.dnetlib.organizations.model.view.UserView;
|
import eu.dnetlib.organizations.model.view.UserView;
|
||||||
|
@ -51,6 +54,7 @@ import eu.dnetlib.organizations.repository.RelationshipRepository;
|
||||||
import eu.dnetlib.organizations.repository.UrlRepository;
|
import eu.dnetlib.organizations.repository.UrlRepository;
|
||||||
import eu.dnetlib.organizations.repository.UserCountryRepository;
|
import eu.dnetlib.organizations.repository.UserCountryRepository;
|
||||||
import eu.dnetlib.organizations.repository.UserRepository;
|
import eu.dnetlib.organizations.repository.UserRepository;
|
||||||
|
import eu.dnetlib.organizations.repository.readonly.OrganizationViewRepository;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class DatabaseUtils {
|
public class DatabaseUtils {
|
||||||
|
@ -75,6 +79,9 @@ public class DatabaseUtils {
|
||||||
private OpenaireConflictRepository openaireConflictRepository;
|
private OpenaireConflictRepository openaireConflictRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OpenaireDuplicateRepository openaireDuplicateRepository;
|
private OpenaireDuplicateRepository openaireDuplicateRepository;
|
||||||
|
@Autowired
|
||||||
|
private OrganizationViewRepository organizationViewRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JdbcTemplate jdbcTemplate;
|
private JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
@ -90,19 +97,29 @@ public class DatabaseUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public String insertOrUpdateOrganization(final OrganizationView orgView, final String user) {
|
public String insertOrUpdateOrganization(final OrganizationView orgView, final String user, final boolean isSimpleUser) {
|
||||||
final boolean alreadyApproved = StringUtils.equals(orgView.getStatus(), OrganizationStatus.approved.toString());
|
|
||||||
|
|
||||||
final String oldId = orgView.getId();
|
final String oldStatus = orgView.getId() != null ? organizationRepository.findById(orgView.getId())
|
||||||
|
.map(Organization::getStatus)
|
||||||
|
.orElse(null) : null;
|
||||||
|
|
||||||
if (StringUtils.isBlank(orgView.getId())) {
|
final boolean alreadyApproved = StringUtils.equals(oldStatus, OrganizationStatus.approved.toString());
|
||||||
orgView.setId(null);
|
|
||||||
} else if (!alreadyApproved) {
|
final String newStatus;
|
||||||
cleanOldRelations(oldId);
|
if (!isSimpleUser) { // IS ADMIN
|
||||||
organizationRepository.deleteById(oldId);
|
newStatus = OrganizationStatus.approved.toString();
|
||||||
orgView.setId(null);
|
} else if (isSimpleUser && oldStatus == null) {
|
||||||
|
newStatus = OrganizationStatus.suggested.toString();
|
||||||
|
} else if (isSimpleUser && alreadyApproved) {
|
||||||
|
newStatus = OrganizationStatus.approved.toString();
|
||||||
} else {
|
} else {
|
||||||
cleanOldRelations(orgView.getId());
|
throw new RuntimeException("User not authorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String oldId = StringUtils.isNotBlank(orgView.getId()) ? new String(orgView.getId()) : null;
|
||||||
|
|
||||||
|
if (oldId == null || !oldId.startsWith(OpenOrgsConstants.OPENORGS_PREFIX)) {
|
||||||
|
orgView.setId(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Organization org = new Organization(orgView.getId(),
|
final Organization org = new Organization(orgView.getId(),
|
||||||
|
@ -110,39 +127,67 @@ public class DatabaseUtils {
|
||||||
orgView.getType(),
|
orgView.getType(),
|
||||||
orgView.getLat(), orgView.getLng(),
|
orgView.getLat(), orgView.getLng(),
|
||||||
orgView.getCity(), orgView.getCountry(),
|
orgView.getCity(), orgView.getCountry(),
|
||||||
OrganizationStatus.approved.toString());
|
newStatus);
|
||||||
|
|
||||||
final String newId = organizationRepository.save(org).getId();
|
final String newId = organizationRepository.save(org).getId();
|
||||||
|
|
||||||
makeNewRelations(newId, orgView);
|
final OffsetDateTime now = OffsetDateTime.now();
|
||||||
|
|
||||||
updateHistoryFields(newId, user, alreadyApproved);
|
organizationRepository.updateModificationDate(newId, user, now);
|
||||||
|
|
||||||
|
if (StringUtils.equals(newId, oldId)) {
|
||||||
|
makeRelations(newId, orgView, true);
|
||||||
|
} else {
|
||||||
|
organizationRepository.updateCreationDate(newId, user, now);
|
||||||
|
makeRelations(newId, orgView, false);
|
||||||
|
if (oldId != null) {
|
||||||
|
|
||||||
|
final List<OpenaireDuplicate> dups = new ArrayList<>();
|
||||||
|
|
||||||
|
dups.add(new OpenaireDuplicate(newId, oldId, SimilarityType.is_similar.toString()));
|
||||||
|
dups.addAll(openaireDuplicateRepository.findByLocalId(oldId)
|
||||||
|
.stream()
|
||||||
|
.map(d -> new OpenaireDuplicate(newId, d.getOaOriginalId(), SimilarityType.suggested.toString()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
|
openaireDuplicateRepository.saveAll(dups);
|
||||||
|
dups.forEach(d -> openaireDuplicateRepository.updateModificationDate(d.getLocalId(), d.getOaOriginalId(), user, now));
|
||||||
|
|
||||||
|
organizationRepository.updateStatus(oldId, OrganizationStatus.duplicate.toString());
|
||||||
|
organizationRepository.updateModificationDate(oldId, user, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newId;
|
return newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateHistoryFields(final String id, final String user, final boolean update) {
|
|
||||||
|
|
||||||
final OffsetDateTime now = OffsetDateTime.now();
|
|
||||||
if (update) {
|
|
||||||
organizationRepository.updateModificationDate(id, user, now);
|
|
||||||
} else {
|
|
||||||
organizationRepository.updateCreationDate(id, user, now);
|
|
||||||
organizationRepository.updateModificationDate(id, user, now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void saveDuplicates(final List<OpenaireDuplicate> simrels, final String email) {
|
public void saveDuplicates(final List<OpenaireDuplicate> simrels, final String user) {
|
||||||
final OffsetDateTime now = OffsetDateTime.now();
|
final OffsetDateTime now = OffsetDateTime.now();
|
||||||
|
|
||||||
final List<OpenaireDuplicate> list = openaireDuplicateRepository.saveAll(simrels);
|
final List<OpenaireDuplicate> list = openaireDuplicateRepository.saveAll(simrels);
|
||||||
|
|
||||||
list.forEach(d -> openaireDuplicateRepository.updateModificationDate(d.getLocalId(), d.getOaOriginalId(), email, now));
|
list.forEach(d -> {
|
||||||
|
openaireDuplicateRepository.updateModificationDate(d.getLocalId(), d.getOaOriginalId(), user, now);
|
||||||
|
|
||||||
|
if (d.getRelType().equals(SimilarityType.is_different.toString())) {
|
||||||
|
updateStatus(d.getOaOriginalId(), OrganizationStatus.suggested, user, now);
|
||||||
|
} else {
|
||||||
|
updateStatus(d.getOaOriginalId(), OrganizationStatus.duplicate, user, now);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeNewRelations(final String orgId, final OrganizationView orgView) {
|
private void makeRelations(final String orgId, final OrganizationView orgView, final boolean update) {
|
||||||
|
if (update) {
|
||||||
|
acronymRepository.deleteByOrgId(orgId);
|
||||||
|
otherNameRepository.deleteByOrgId(orgId);
|
||||||
|
otherIdentifierRepository.deleteByOrgId(orgId);
|
||||||
|
urlRepository.deleteByOrgId(orgId);
|
||||||
|
relationshipRepository.deleteById1(orgId);
|
||||||
|
relationshipRepository.deleteById2(orgId);
|
||||||
|
}
|
||||||
orgView.getAcronyms().forEach(s -> acronymRepository.save(new Acronym(orgId, s)));
|
orgView.getAcronyms().forEach(s -> acronymRepository.save(new Acronym(orgId, s)));
|
||||||
orgView.getOtherNames().forEach(n -> otherNameRepository.save(new OtherName(orgId, n.getName(), n.getLang())));
|
orgView.getOtherNames().forEach(n -> otherNameRepository.save(new OtherName(orgId, n.getName(), n.getLang())));
|
||||||
orgView.getOtherIdentifiers().forEach(id -> otherIdentifierRepository.save(new OtherIdentifier(orgId, id.getId(), id.getType())));
|
orgView.getOtherIdentifiers().forEach(id -> otherIdentifierRepository.save(new OtherIdentifier(orgId, id.getId(), id.getType())));
|
||||||
|
@ -150,15 +195,6 @@ public class DatabaseUtils {
|
||||||
orgView.getRelations().forEach(r -> makeRelation(orgId, r.getRelatedOrgId(), RelationType.valueOf(r.getType())));
|
orgView.getRelations().forEach(r -> makeRelation(orgId, r.getRelatedOrgId(), RelationType.valueOf(r.getType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanOldRelations(final String id) {
|
|
||||||
acronymRepository.deleteByOrgId(id);
|
|
||||||
otherNameRepository.deleteByOrgId(id);
|
|
||||||
otherIdentifierRepository.deleteByOrgId(id);
|
|
||||||
urlRepository.deleteByOrgId(id);
|
|
||||||
relationshipRepository.deleteById1(id);
|
|
||||||
relationshipRepository.deleteById2(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Cacheable("vocs")
|
@Cacheable("vocs")
|
||||||
public List<VocabularyTerm> listValuesOfVocabularyTable(final VocabularyTable table) {
|
public List<VocabularyTerm> listValuesOfVocabularyTable(final VocabularyTable table) {
|
||||||
final String sql = "select val as value, name as name from " + table;
|
final String sql = "select val as value, name as name from " + table;
|
||||||
|
@ -203,54 +239,30 @@ public class DatabaseUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
/*
|
||||||
public void verifyConflictGroups(final boolean forceUpdate) {
|
* @Transactional public void verifyConflictGroups(final boolean forceUpdate) {
|
||||||
|
*
|
||||||
if (forceUpdate || openaireConflictRepository.countByGroupNull() > 0) {
|
* if (forceUpdate || openaireConflictRepository.countByGroupNull() > 0) {
|
||||||
|
*
|
||||||
log.info("Recreating conflicts group...");
|
* log.info("Recreating conflicts group...");
|
||||||
|
*
|
||||||
openaireConflictRepository.resetGroupIds();
|
* openaireConflictRepository.resetGroupIds();
|
||||||
|
*
|
||||||
final Map<String, Set<String>> groups = new HashMap<>();
|
* final Map<String, Set<String>> groups = new HashMap<>(); for (final OpenaireConflict w : openaireConflictRepository.findAll()) {
|
||||||
for (final OpenaireConflict w : openaireConflictRepository.findAll()) {
|
* final List<String> list = findExistingGroupsForRel(w, groups); if (list.isEmpty()) { final String idGroup = generateGroupId();
|
||||||
final List<String> list = findExistingGroupsForRel(w, groups);
|
* groups.put(idGroup, new HashSet<>()); addToGroup(groups, idGroup, w); } else if (list.size() == 1) { addToGroup(groups, list.get(0),
|
||||||
if (list.isEmpty()) {
|
* w); } else { final String idGroup = generateGroupId(); groups.put(idGroup, new TreeSet<>()); list.forEach(id ->
|
||||||
final String idGroup = generateGroupId();
|
* groups.get(idGroup).addAll(groups.get(id))); list.forEach(id -> groups.remove(id)); addToGroup(groups, idGroup, w); } }
|
||||||
groups.put(idGroup, new HashSet<>());
|
*
|
||||||
addToGroup(groups, idGroup, w);
|
* for (final Entry<String, Set<String>> e : groups.entrySet()) { final String gid = e.getKey(); for (final String orgId : e.getValue())
|
||||||
} else if (list.size() == 1) {
|
* { for (final OpenaireConflict oc : openaireConflictRepository.findById1AndGroupIsNull(orgId)) { oc.setGroup(gid);
|
||||||
addToGroup(groups, list.get(0), w);
|
* openaireConflictRepository.save(oc); } for (final OpenaireConflict oc : openaireConflictRepository.findById2AndGroupIsNull(orgId)) {
|
||||||
} else {
|
* oc.setGroup(gid); openaireConflictRepository.save(oc); } } }
|
||||||
final String idGroup = generateGroupId();
|
*
|
||||||
groups.put(idGroup, new TreeSet<>());
|
* log.info("...conflict groups recreated"); } }
|
||||||
list.forEach(id -> groups.get(idGroup).addAll(groups.get(id)));
|
*
|
||||||
list.forEach(id -> groups.remove(id));
|
* private String generateGroupId() { return "group::" + UUID.randomUUID(); }
|
||||||
addToGroup(groups, idGroup, w);
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Entry<String, Set<String>> e : groups.entrySet()) {
|
|
||||||
final String gid = e.getKey();
|
|
||||||
for (final String orgId : e.getValue()) {
|
|
||||||
for (final OpenaireConflict oc : openaireConflictRepository.findById1AndGroupIsNull(orgId)) {
|
|
||||||
oc.setGroup(gid);
|
|
||||||
openaireConflictRepository.save(oc);
|
|
||||||
}
|
|
||||||
for (final OpenaireConflict oc : openaireConflictRepository.findById2AndGroupIsNull(orgId)) {
|
|
||||||
oc.setGroup(gid);
|
|
||||||
openaireConflictRepository.save(oc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("...conflicts group recreated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateGroupId() {
|
|
||||||
return "group::" + UUID.randomUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> findExistingGroupsForRel(final OpenaireConflict w, final Map<String, Set<String>> groups) {
|
private List<String> findExistingGroupsForRel(final OpenaireConflict w, final Map<String, Set<String>> groups) {
|
||||||
return groups.entrySet()
|
return groups.entrySet()
|
||||||
|
@ -280,36 +292,51 @@ public class DatabaseUtils {
|
||||||
// BROWSE BY COUNTRY
|
// BROWSE BY COUNTRY
|
||||||
public List<BrowseEntry> browseCountries() {
|
public List<BrowseEntry> browseCountries() {
|
||||||
final String sql =
|
final String sql =
|
||||||
"select o.country as value, c.name as name, sum(case when status='approved' then 1 else 0 end) as approved, sum(case when status='pending' then 1 else 0 end) as pending from organizations o left outer join countries c on (o.country = c.val) group by o.country, c.name order by approved desc";
|
"select o.country as code, c.name as name, o.status as group, count(o.status) as count from organizations o left outer join countries c on (o.country = c.val) group by o.country, c.name, o.status";
|
||||||
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class));
|
return listBrowseEntries(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
// BROWSE BY COUNTRY FOR USER
|
// BROWSE BY COUNTRY FOR USER
|
||||||
public List<BrowseEntry> browseCountriesForUser(final String email) {
|
public List<BrowseEntry> browseCountriesForUser(final String email) {
|
||||||
final String sql =
|
final String sql =
|
||||||
"select o.country as value, c.name as name, sum(case when status='approved' then 1 else 0 end) as approved, sum(case when status='pending' then 1 else 0 end) as pending from user_countries uc left outer join organizations o on (uc.country = o.country) left outer join countries c on (o.country = c.val) where uc.email=? group by o.country, c.name order by approved desc";
|
"select o.country as code, c.name as name, o.status as group, count(o.status) as count from user_countries uc left outer join organizations o on (uc.country = o.country) left outer join countries c on (o.country = c.val) where uc.email=? group by o.country, c.name, o.status";
|
||||||
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class), email);
|
return listBrowseEntries(sql, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
// BROWSE BY ORG TYPE
|
// BROWSE BY ORG TYPE
|
||||||
public List<BrowseEntry> browseTypes() {
|
public List<BrowseEntry> browseTypes() {
|
||||||
final String sql =
|
final String sql =
|
||||||
"select type as value, type as name, sum(case when status='approved' then 1 else 0 end) as approved, sum(case when status='pending' then 1 else 0 end) as pending from organizations group by type order by approved desc";
|
"select type as code, type as name, status as group, count(status) as count from organizations group by type, status";
|
||||||
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class));
|
return listBrowseEntries(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
// BROWSE BY ORG TYPE FOR USER
|
// BROWSE BY ORG TYPE FOR USER
|
||||||
public List<BrowseEntry> browseTypesForUser(final String email) {
|
public List<BrowseEntry> browseTypesForUser(final String email) {
|
||||||
final String sql = "select o.type as value, o.type as name,"
|
final String sql = "select o.type as code, o.type as name,"
|
||||||
+ "sum(case when status='approved' then 1 else 0 end) as approved, "
|
+ "o.status as group, count(o.status) as count "
|
||||||
+ "sum(case when status='pending' then 1 else 0 end) as pending "
|
|
||||||
+ "from organizations o "
|
+ "from organizations o "
|
||||||
+ "left outer join user_countries uc on (uc.country = o.country) "
|
+ "left outer join user_countries uc on (uc.country = o.country) "
|
||||||
+ "where uc.email=? "
|
+ "where uc.email=? "
|
||||||
+ "group by o.type "
|
+ "group by o.type, o.status";
|
||||||
+ "order by approved desc;";
|
return listBrowseEntries(sql, email);
|
||||||
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class), email);
|
}
|
||||||
|
|
||||||
|
private List<BrowseEntry> listBrowseEntries(final String sql, final Object... params) {
|
||||||
|
final Map<String, BrowseEntry> map = new HashMap<>();
|
||||||
|
|
||||||
|
for (final TempBrowseEntry t : jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TempBrowseEntry.class), params)) {
|
||||||
|
if (StringUtils.isNotBlank(t.getCode())) {
|
||||||
|
if (!map.containsKey(t.getCode())) {
|
||||||
|
final BrowseEntry e = new BrowseEntry();
|
||||||
|
e.setCode(t.getCode());
|
||||||
|
e.setName(t.getName());
|
||||||
|
map.put(t.getCode(), e);
|
||||||
|
}
|
||||||
|
map.get(t.getCode()).getValues().put(t.getGroup(), t.getCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map.values().stream().sorted((o1, o2) -> StringUtils.compare(o1.getName(), o2.getName())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<OrganizationConflict> listConflictsForId(final String id) {
|
public List<OrganizationConflict> listConflictsForId(final String id) {
|
||||||
|
@ -321,9 +348,10 @@ public class DatabaseUtils {
|
||||||
@Transactional
|
@Transactional
|
||||||
public void importDedupEvents() {
|
public void importDedupEvents() {
|
||||||
try {
|
try {
|
||||||
// log.info("Importing conflicts and duplicates...");
|
log.info("Importing conflicts and duplicates...");
|
||||||
// jdbcTemplate.update(IOUtils.toString(getClass().getResourceAsStream("/sql/importNewRels.sql")));
|
jdbcTemplate.update(IOUtils.toString(getClass().getResourceAsStream("/sql/importDedupEvents.sql")));
|
||||||
// log.info("...done");
|
log.info("...done");
|
||||||
|
|
||||||
// verifyConflictGroups(true);
|
// verifyConflictGroups(true);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Error importing conflicts and duplicates", e);
|
log.error("Error importing conflicts and duplicates", e);
|
||||||
|
@ -331,13 +359,98 @@ public class DatabaseUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<Relationship> fixDuplicate(final String masterId, final String otherId) {
|
public OrganizationView markAsDeleted(final String id, final String user) {
|
||||||
|
final OffsetDateTime now = OffsetDateTime.now();
|
||||||
|
updateStatus(id, OrganizationStatus.deleted, user, now);
|
||||||
|
return organizationViewRepository.findById(id).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public OrganizationView markAsDiscarded(final String id, final String user) {
|
||||||
|
final OffsetDateTime now = OffsetDateTime.now();
|
||||||
|
updateStatus(id, OrganizationStatus.discarded, user, now);
|
||||||
|
|
||||||
|
openaireDuplicateRepository.findByLocalId(id).forEach(d -> updateStatus(d.getOaOriginalId(), OrganizationStatus.suggested, user, now));
|
||||||
|
|
||||||
|
return organizationViewRepository.findById(id).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatus(final String id, final OrganizationStatus status, final String user, final OffsetDateTime now) {
|
||||||
|
organizationRepository.updateStatus(id, status.toString());
|
||||||
|
organizationRepository.updateModificationDate(id, user, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public String fixConflict(final List<String> ids, final String user) {
|
||||||
|
|
||||||
|
final List<OrganizationView> views =
|
||||||
|
ids.stream().map(organizationViewRepository::findById).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// I create a new org
|
||||||
|
final OrganizationView newOrg = new OrganizationView();
|
||||||
|
newOrg.setId(null);
|
||||||
|
newOrg.setStatus(null);
|
||||||
|
newOrg.setName(findFirstString(views, OrganizationView::getName));
|
||||||
|
newOrg.setType(findFirstString(views, OrganizationView::getType));
|
||||||
|
newOrg.setLat(findFirstNumber(views, OrganizationView::getLat));
|
||||||
|
newOrg.setLng(findFirstNumber(views, OrganizationView::getLng));
|
||||||
|
newOrg.setCity(findFirstString(views, OrganizationView::getCity));
|
||||||
|
newOrg.setCountry(findFirstString(views, OrganizationView::getCountry));
|
||||||
|
newOrg.setOtherIdentifiers(findAll(views, OrganizationView::getOtherIdentifiers));
|
||||||
|
newOrg.setOtherNames(findAll(views, OrganizationView::getOtherNames));
|
||||||
|
newOrg.setAcronyms(findAll(views, OrganizationView::getAcronyms));
|
||||||
|
newOrg.setUrls(findAll(views, OrganizationView::getUrls));
|
||||||
|
newOrg.setRelations(findAll(views, OrganizationView::getRelations));
|
||||||
|
|
||||||
|
newOrg.getOtherNames()
|
||||||
|
.addAll(views.stream()
|
||||||
|
.map(OrganizationView::getName)
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.filter(s -> StringUtils.equalsIgnoreCase(s, newOrg.getName()))
|
||||||
|
.map(s -> new eu.dnetlib.organizations.model.view.OtherName(s, "UNKNOWN"))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
|
final String masterId = insertOrUpdateOrganization(newOrg, user, false);
|
||||||
|
|
||||||
|
// I hide the merged organizations
|
||||||
|
ids.forEach(id -> hideConflictOrgs(masterId, id));
|
||||||
|
|
||||||
|
// I reassign the duplicated to the new org
|
||||||
|
final List<OpenaireDuplicate> newDuplicates = ids.stream()
|
||||||
|
.map(openaireDuplicateRepository::findByLocalId)
|
||||||
|
.flatMap(l -> l.stream())
|
||||||
|
.filter(d -> d.getRelType().equals(SimilarityType.is_similar.toString()))
|
||||||
|
.map(d -> new OpenaireDuplicate(masterId, d.getOaOriginalId(), d.getRelType()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
newDuplicates.forEach(d -> d.setLocalId(masterId));
|
||||||
|
|
||||||
|
final OffsetDateTime now = OffsetDateTime.now();
|
||||||
|
|
||||||
|
for (int i = 0; i < ids.size(); i++) {
|
||||||
|
for (int j = i + 1; j < ids.size(); j++) {
|
||||||
|
openaireConflictRepository.updateStatus(ids.get(i), ids.get(j), SimilarityType.is_similar.toString(), user, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return masterId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findFirstString(final List<OrganizationView> views, final Function<OrganizationView, String> mapper) {
|
||||||
|
return views.stream().map(mapper).filter(StringUtils::isNotBlank).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Double findFirstNumber(final List<OrganizationView> views, final Function<OrganizationView, Double> mapper) {
|
||||||
|
return views.stream().map(mapper).filter(Objects::nonNull).filter(n -> n != 0).findFirst().orElse(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Set<T> findAll(final List<OrganizationView> views, final Function<OrganizationView, Set<T>> mapper) {
|
||||||
|
return views.stream().map(mapper).flatMap(s -> s.stream()).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Relationship> hideConflictOrgs(final String masterId, final String otherId) {
|
||||||
organizationRepository.updateStatus(otherId, OrganizationStatus.hidden.toString());
|
organizationRepository.updateStatus(otherId, OrganizationStatus.hidden.toString());
|
||||||
openaireConflictRepository.findById(new OpenaireConflictPK(masterId, otherId)).ifPresent(openaireConflictRepository::delete);
|
openaireConflictRepository.findById(new OpenaireConflictPK(masterId, otherId)).ifPresent(openaireConflictRepository::delete);
|
||||||
openaireConflictRepository.findById(new OpenaireConflictPK(otherId, masterId)).ifPresent(openaireConflictRepository::delete);
|
openaireConflictRepository.findById(new OpenaireConflictPK(otherId, masterId)).ifPresent(openaireConflictRepository::delete);
|
||||||
|
|
||||||
// TODO Merge the organizations ???
|
|
||||||
|
|
||||||
return makeRelation(masterId, otherId, RelationType.Merges);
|
return makeRelation(masterId, otherId, RelationType.Merges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package eu.dnetlib.organizations.utils;
|
package eu.dnetlib.organizations.utils;
|
||||||
|
|
||||||
public enum OrganizationStatus {
|
public enum OrganizationStatus {
|
||||||
pending,
|
suggested, // from user or dedup depends by created_by field
|
||||||
approved,
|
approved, // normal status of valid organizations
|
||||||
discarded,
|
discarded, // suggested organization that have been rejected by an administrator
|
||||||
hidden,
|
hidden, // hidden organizations after the fix of a conflict
|
||||||
deleted,
|
deleted, // organizations that are virtually deleted
|
||||||
duplicate
|
duplicate // organizations that duplicate of a valid organization (their id is not an openorgs id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ spring.jpa.properties.hibernate.hbm2dll.extra_physical_table_types = MATERIALIZE
|
||||||
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
|
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
|
||||||
spring.jpa.open-in-view=true
|
spring.jpa.open-in-view=true
|
||||||
|
|
||||||
spring.jpa.properties.hibernate.show_sql=true
|
spring.jpa.properties.hibernate.show_sql=false
|
||||||
spring.jpa.properties.hibernate.use_sql_comments=true
|
spring.jpa.properties.hibernate.use_sql_comments=false
|
||||||
spring.jpa.properties.hibernate.format_sql=true
|
spring.jpa.properties.hibernate.format_sql=false
|
||||||
|
|
||||||
# the ICM private network
|
# the ICM private network
|
||||||
openaire.api.valid.subnet = 10.19.65.0/24
|
openaire.api.valid.subnet = 10.19.65.0/24
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DELETE FROM oa_conflicts WHERE created_by = 'dedupWf' and reltype = 'suggested';
|
||||||
|
DELETE FROM oa_duplicates WHERE created_by = 'dedupWf' and reltype = 'suggested';
|
||||||
|
DELETE FROM organizations WHERE created_by = 'dedupWf' and modified_by = 'dedupWf';
|
||||||
|
|
||||||
|
-- FIX IMPORT DATA
|
||||||
|
UPDATE tmp_dedup_events SET oa_country = 'UNKNOWN' WHERE oa_country = '' OR oa_country IS NULL;
|
||||||
|
|
||||||
|
-- NEW ORGANIZATIONS
|
||||||
|
INSERT INTO organizations(id, name, country, status, created_by, modified_by)
|
||||||
|
SELECT oa_original_id, oa_name, oa_country, 'suggested', 'dedupWf', 'dedupWf'
|
||||||
|
FROM tmp_dedup_events
|
||||||
|
WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' AND oa_original_id = local_id
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO organizations(id, name, country, status, created_by, modified_by)
|
||||||
|
SELECT oa_original_id, oa_name, oa_country, 'duplicate', 'dedupWf', 'dedupWf'
|
||||||
|
FROM tmp_dedup_events
|
||||||
|
WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' AND oa_original_id != local_id
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO acronyms(id, acronym) SELECT oa_original_id, oa_acronym FROM tmp_dedup_events WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' ON CONFLICT DO NOTHING;
|
||||||
|
INSERT INTO urls(id, url) SELECT oa_original_id, oa_url FROM tmp_dedup_events WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- DUPLICATES
|
||||||
|
INSERT INTO oa_duplicates (local_id, oa_original_id, oa_collectedfrom, created_by, modified_by)
|
||||||
|
SELECT local_id, oa_original_id, oa_collectedfrom, 'dedupWf', 'dedupWf'
|
||||||
|
FROM tmp_dedup_events
|
||||||
|
WHERE local_id IS NOT NULL AND local_id != '' AND oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' AND local_id != oa_original_id
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
|
||||||
|
-- CONFLICTS
|
||||||
|
INSERT INTO oa_conflicts (id1, id2, idgroup, created_by, modified_by)
|
||||||
|
SELECT local_id, oa_original_id, group_id, 'dedupWf', 'dedupWf'
|
||||||
|
FROM tmp_dedup_events
|
||||||
|
WHERE local_id LIKE 'openorgs\_\_\_\_::%' AND oa_original_id LIKE 'openorgs\_\_\_\_::%' AND local_id != oa_original_id AND group_id IS NOT NULL AND group_id != ''
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
|
||||||
|
COMMIT;
|
|
@ -1,15 +0,0 @@
|
||||||
DELETE FROM oa_duplicates WHERE reltype = 'suggested';
|
|
||||||
DELETE FROM oa_conflicts WHERE reltype = 'suggested';
|
|
||||||
UPDATE oa_conflicts SET idgroup = NULL;
|
|
||||||
|
|
||||||
INSERT INTO oa_duplicates (local_id, oa_original_id, oa_name, oa_acronym, oa_country, oa_url, oa_collectedfrom)
|
|
||||||
SELECT local_id, oa_original_id, oa_name, oa_acronym, oa_country, oa_url, oa_collectedfrom
|
|
||||||
FROM tmp_simrels
|
|
||||||
WHERE oa_original_id NOT LIKE 'openorgs____::%'
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
INSERT INTO oa_conflicts (id1, id2)
|
|
||||||
SELECT local_id, oa_original_id
|
|
||||||
FROM tmp_simrels
|
|
||||||
WHERE oa_original_id LIKE 'openorgs____::%'
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
|
@ -335,13 +335,13 @@ CREATE TABLE organizations (
|
||||||
creation_date timestamp with time zone DEFAULT now(),
|
creation_date timestamp with time zone DEFAULT now(),
|
||||||
modified_by text,
|
modified_by text,
|
||||||
modification_date timestamp with time zone DEFAULT now(),
|
modification_date timestamp with time zone DEFAULT now(),
|
||||||
status text NOT NULL DEFAULT 'pending'
|
status text NOT NULL DEFAULT 'suggested'
|
||||||
);
|
);
|
||||||
CREATE INDEX organizations_type_idx ON organizations(type);
|
CREATE INDEX organizations_type_idx ON organizations(type);
|
||||||
CREATE INDEX organizations_country_idx ON organizations(country);
|
CREATE INDEX organizations_country_idx ON organizations(country);
|
||||||
|
|
||||||
CREATE TABLE other_ids (
|
CREATE TABLE other_ids (
|
||||||
id text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
otherid text,
|
otherid text,
|
||||||
type text REFERENCES id_types(val),
|
type text REFERENCES id_types(val),
|
||||||
PRIMARY KEY (id, otherid, type)
|
PRIMARY KEY (id, otherid, type)
|
||||||
|
@ -349,7 +349,7 @@ CREATE TABLE other_ids (
|
||||||
CREATE INDEX other_ids_id_idx ON other_ids(id);
|
CREATE INDEX other_ids_id_idx ON other_ids(id);
|
||||||
|
|
||||||
CREATE TABLE other_names (
|
CREATE TABLE other_names (
|
||||||
id text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
name text,
|
name text,
|
||||||
lang text REFERENCES languages(val),
|
lang text REFERENCES languages(val),
|
||||||
PRIMARY KEY (id, name, lang)
|
PRIMARY KEY (id, name, lang)
|
||||||
|
@ -357,31 +357,31 @@ CREATE TABLE other_names (
|
||||||
CREATE INDEX other_names_id_idx ON other_names(id);
|
CREATE INDEX other_names_id_idx ON other_names(id);
|
||||||
|
|
||||||
CREATE TABLE acronyms (
|
CREATE TABLE acronyms (
|
||||||
id text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
acronym text,
|
acronym text,
|
||||||
PRIMARY KEY (id, acronym)
|
PRIMARY KEY (id, acronym)
|
||||||
);
|
);
|
||||||
CREATE INDEX acronyms_id_idx ON acronyms(id);
|
CREATE INDEX acronyms_id_idx ON acronyms(id);
|
||||||
|
|
||||||
CREATE TABLE relationships (
|
CREATE TABLE relationships (
|
||||||
id1 text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id1 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
reltype text,
|
reltype text,
|
||||||
id2 text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id2 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
PRIMARY KEY (id1, reltype, id2)
|
PRIMARY KEY (id1, reltype, id2)
|
||||||
);
|
);
|
||||||
CREATE INDEX relationships_id1_idx ON relationships(id1);
|
CREATE INDEX relationships_id1_idx ON relationships(id1);
|
||||||
CREATE INDEX relationships_id2_idx ON relationships(id2);
|
CREATE INDEX relationships_id2_idx ON relationships(id2);
|
||||||
|
|
||||||
CREATE TABLE urls (
|
CREATE TABLE urls (
|
||||||
id text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
url text,
|
url text,
|
||||||
PRIMARY KEY (id, url)
|
PRIMARY KEY (id, url)
|
||||||
);
|
);
|
||||||
CREATE INDEX urls_id_idx ON urls(id);
|
CREATE INDEX urls_id_idx ON urls(id);
|
||||||
|
|
||||||
CREATE TABLE oa_duplicates (
|
CREATE TABLE oa_duplicates (
|
||||||
local_id text REFERENCES organizations(id) ON UPDATE CASCADE,
|
local_id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
oa_original_id text REFERENCES organizations(id) ON UPDATE CASCADE,
|
oa_original_id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
oa_name text NOT NULL,
|
oa_name text NOT NULL,
|
||||||
oa_acronym text,
|
oa_acronym text,
|
||||||
oa_country text,
|
oa_country text,
|
||||||
|
@ -389,6 +389,7 @@ CREATE TABLE oa_duplicates (
|
||||||
oa_collectedfrom text,
|
oa_collectedfrom text,
|
||||||
reltype text NOT NULL DEFAULT 'suggested',
|
reltype text NOT NULL DEFAULT 'suggested',
|
||||||
creation_date timestamp DEFAULT NOW(),
|
creation_date timestamp DEFAULT NOW(),
|
||||||
|
created_by text,
|
||||||
modification_date timestamp,
|
modification_date timestamp,
|
||||||
modified_by text,
|
modified_by text,
|
||||||
PRIMARY KEY (local_id, oa_original_id)
|
PRIMARY KEY (local_id, oa_original_id)
|
||||||
|
@ -419,11 +420,12 @@ GROUP BY
|
||||||
d.reltype;
|
d.reltype;
|
||||||
|
|
||||||
CREATE TABLE oa_conflicts (
|
CREATE TABLE oa_conflicts (
|
||||||
id1 text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id1 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
id2 text REFERENCES organizations(id) ON UPDATE CASCADE,
|
id2 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
reltype text NOT NULL DEFAULT 'suggested',
|
reltype text NOT NULL DEFAULT 'suggested',
|
||||||
idgroup text,
|
idgroup text,
|
||||||
creation_date timestamp DEFAULT NOW(),
|
creation_date timestamp DEFAULT NOW(),
|
||||||
|
created_by text,
|
||||||
modification_date timestamp,
|
modification_date timestamp,
|
||||||
modified_by text,
|
modified_by text,
|
||||||
PRIMARY KEY (id1, id2)
|
PRIMARY KEY (id1, id2)
|
||||||
|
@ -485,10 +487,12 @@ CREATE VIEW organizations_simple_view AS SELECT
|
||||||
org.city,
|
org.city,
|
||||||
org.country,
|
org.country,
|
||||||
org.status,
|
org.status,
|
||||||
array_remove(array_agg(DISTINCT a.acronym), NULL) AS acronyms
|
array_remove(array_agg(DISTINCT a.acronym), NULL) AS acronyms,
|
||||||
|
array_remove(array_agg(DISTINCT u.url), NULL) AS urls
|
||||||
FROM
|
FROM
|
||||||
organizations org
|
organizations org
|
||||||
LEFT OUTER JOIN acronyms a ON (org.id = a.id)
|
LEFT OUTER JOIN acronyms a ON (org.id = a.id)
|
||||||
|
LEFT OUTER JOIN urls u ON (org.id = u.id)
|
||||||
GROUP BY
|
GROUP BY
|
||||||
org.id,
|
org.id,
|
||||||
org.name,
|
org.name,
|
||||||
|
@ -513,9 +517,9 @@ CREATE VIEW suggestions_info_by_country_view AS SELECT c.val AS country,
|
||||||
coalesce(t2.n_conflicts, 0) AS n_conflicts,
|
coalesce(t2.n_conflicts, 0) AS n_conflicts,
|
||||||
coalesce(t3.n_pending_orgs, 0) AS n_pending_orgs
|
coalesce(t3.n_pending_orgs, 0) AS n_pending_orgs
|
||||||
FROM countries c
|
FROM countries c
|
||||||
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT d.*) AS n_duplicates FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (d.local_id = o.id) WHERE d.reltype = 'suggested' GROUP BY o.country) AS t1 ON (t1.country = c.val)
|
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT d.*) AS n_duplicates FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (d.local_id = o.id) WHERE d.reltype = 'suggested' AND o.status = 'approved' GROUP BY o.country) AS t1 ON (t1.country = c.val)
|
||||||
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT c.idgroup) AS n_conflicts FROM oa_conflicts c LEFT OUTER JOIN organizations o ON (c.id1 = o.id) WHERE c.reltype = 'suggested' GROUP BY o.country) AS t2 ON (t2.country = c.val)
|
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT c.idgroup) AS n_conflicts FROM oa_conflicts c LEFT OUTER JOIN organizations o ON (c.id1 = o.id) WHERE c.reltype = 'suggested' AND o.status = 'approved' GROUP BY o.country) AS t2 ON (t2.country = c.val)
|
||||||
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT o.id) AS n_pending_orgs FROM organizations o WHERE o.status = 'pending' GROUP BY o.country) AS t3 ON (t3.country = c.val);
|
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT o.id) AS n_pending_orgs FROM organizations o WHERE o.status = 'suggested' GROUP BY o.country) AS t3 ON (t3.country = c.val);
|
||||||
|
|
||||||
CREATE VIEW conflict_groups_view AS SELECT
|
CREATE VIEW conflict_groups_view AS SELECT
|
||||||
c.idgroup AS idgroup,
|
c.idgroup AS idgroup,
|
||||||
|
@ -534,7 +538,12 @@ FROM
|
||||||
LEFT OUTER JOIN organizations o1 ON (c.id1 = o1.id)
|
LEFT OUTER JOIN organizations o1 ON (c.id1 = o1.id)
|
||||||
LEFT OUTER JOIN organizations o2 ON (c.id2 = o2.id)
|
LEFT OUTER JOIN organizations o2 ON (c.id2 = o2.id)
|
||||||
WHERE
|
WHERE
|
||||||
o1.id IS NOT NULL AND O2.id IS NOT NULL AND c.idgroup IS NOT NULL;
|
o1.id IS NOT NULL
|
||||||
|
AND o2.id IS NOT NULL
|
||||||
|
AND o1.status = 'approved'
|
||||||
|
AND o2.status = 'approved'
|
||||||
|
AND c.idgroup IS NOT NULL
|
||||||
|
AND c.reltype = 'suggested';
|
||||||
|
|
||||||
CREATE VIEW duplicate_groups_view AS SELECT
|
CREATE VIEW duplicate_groups_view AS SELECT
|
||||||
o.id,
|
o.id,
|
||||||
|
@ -546,7 +555,7 @@ FROM
|
||||||
oa_duplicates d
|
oa_duplicates d
|
||||||
LEFT OUTER JOIN organizations o ON (o.id = d.local_id)
|
LEFT OUTER JOIN organizations o ON (o.id = d.local_id)
|
||||||
WHERE
|
WHERE
|
||||||
d.reltype = 'suggested'
|
d.reltype = 'suggested' AND o.status = 'approved'
|
||||||
GROUP BY o.id, o.name, o.city, o.country
|
GROUP BY o.id, o.name, o.city, o.country
|
||||||
ORDER BY o.name;
|
ORDER BY o.name;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<table class="table table-sm table-hover col-lg-5">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr>
|
|
||||||
<th>{{field}}</th>
|
|
||||||
<th class="text-right text-nowrap"># approved</th>
|
|
||||||
<th class="text-right text-nowrap"># pending</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="e in entries">
|
|
||||||
<td><a href="#!{{resultsBasePath}}/0/50/{{e.value}}">{{e.name}}<span ng-if="e.value != e.name"> ({{e.value}})</span></a></td>
|
|
||||||
<td class="text-right">{{e.approved}}</td>
|
|
||||||
<td class="text-right">{{e.pending}}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<h4>{{info.name}}</h4>
|
|
||||||
|
|
||||||
<div class="alert alert-success" ng-if="message">{{message}}</div>
|
|
||||||
|
|
||||||
<p class="text-muted" ng-if="!message">
|
|
||||||
<b>ID: </b>{{info.id}}<br /> <b>Created at</b> {{info.creationDate | date:'MMMM d, y HH:mm:ss'}} <b>by</b> {{info.createdBy}}<br /> <b>Modified at</b> {{info.modificationDate | date:'MMMM d, y HH:mm:ss'}} <b>by</b>
|
|
||||||
{{info.modifiedBy}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<org-tabs-menu org-id="{{orgId}}" info="info" org="org" events="events" selected="currentTab"></org-tabs-menu>
|
|
||||||
|
|
||||||
<org-form-metadata org-id="{{orgId}}" org="org" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 1 && org.status == 'approved'" mode="update"></org-form-metadata>
|
|
||||||
<org-form-metadata org-id="{{orgId}}" org="org" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 1 && org.status == 'pending'" mode="approve"></org-form-metadata>
|
|
||||||
<org-form-metadata org-id="{{orgId}}" org="org" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 1 && org.status != 'approved' && org.status != 'pending'" mode="readonly"></org-form-metadata>
|
|
||||||
|
|
||||||
<org-dedup-events org-id="{{orgId}}" events="events" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 2 && (org.status == 'approved' || org.status == 'pending')"></org-dedup-events>
|
|
||||||
</div>
|
|
|
@ -1,42 +0,0 @@
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="text-muted" ng-if="conflicts.length == 0">No suggestions</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-3" ng-show="conflicts.length > 0">
|
|
||||||
<input type="text" class="form-control" ng-model="conflictFilter" placeholder="Filter...">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<span class="input-group-text text-outline-primary">Country:</span>
|
|
||||||
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
|
|
||||||
<div class="dropdown-menu">
|
|
||||||
<small>
|
|
||||||
<a class="dropdown-item" href="#!/suggestions/{{c}}/2"
|
|
||||||
ng-repeat="(c, vals) in info.byCountry"
|
|
||||||
ng-if="vals.nConflicts > 0">
|
|
||||||
{{c}} <span class="badge badge-danger float-right">{{vals.nConflicts}}</span>
|
|
||||||
</a>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card text-white mb-3" ng-repeat="w in conflicts | filter:conflictFilter" class="mb-2">
|
|
||||||
<div class="card-header bg-primary text-white py-1">Group {{$index+1}}</div>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<tr ng-repeat="o in w">
|
|
||||||
<th style="width:40px" class="text-center">#{{$index+1}}</th>
|
|
||||||
<td><a href="#!/edit/0/{{o.id}}" title="{{o.id}}">{{o.name}}</a></td>
|
|
||||||
<td style="width:250px"><img ng-src="resources/images/flags/{{o.country}}.gif" /> {{o.city || '-'}}, {{o.country}}</td>
|
|
||||||
<td style="width:80px" class="text-right">{{o.type}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<div class="card-footer bg-secondary py-1">
|
|
||||||
<button type="button"
|
|
||||||
class="btn btn-sm btn-primary"
|
|
||||||
data-toggle="modal" data-target="#resolveConflictsModal"
|
|
||||||
ng-click="prepareConflictsModal(w)">resolve</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<resolve-conflicts-modal modal-id="resolveConflictsModal" orgs="orgs" selected-orgs="selectedOrgs"></resolve-conflicts-modal>
|
|
|
@ -1,62 +0,0 @@
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="text-muted" ng-if="duplicates.length == 0">No suggestions</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-3" ng-show="duplicates.length > 0">
|
|
||||||
<input type="text" class="form-control" ng-model="duplicateFilter" placeholder="Filter...">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<span class="input-group-text text-outline-primary">Country:</span>
|
|
||||||
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
|
|
||||||
<div class="dropdown-menu">
|
|
||||||
<small>
|
|
||||||
<a class="dropdown-item" href="#!/suggestions/{{c}}/1"
|
|
||||||
ng-repeat="(c, vals) in info.byCountry"
|
|
||||||
ng-if="vals.nDuplicates > 0">
|
|
||||||
{{c}} <span class="badge badge-primary float-right">{{vals.nDuplicates}}</span>
|
|
||||||
</a>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="table table-sm table-hover" ng-if="duplicates.length > 0">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr class="d-flex">
|
|
||||||
<th class="col-8">Organization</th>
|
|
||||||
<th class="col-3">Place</th>
|
|
||||||
<th class="col-1 text-right"># pending duplicates</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="d in duplicates | filter:duplicateFilter" class="d-flex">
|
|
||||||
<td class="col-8">
|
|
||||||
<a href="javascript:void(0)" title="{{d.id}}" ng-click="prepareDuplicatesModal(d)" data-toggle="modal" data-target="#duplicatesModal">{{d.name}}</a>
|
|
||||||
<a href="#!/edit/0/{{d.id}}" title="edit"><i class="fa fa-edit"></i></a>
|
|
||||||
</td>
|
|
||||||
<td class="col-3"><img ng-src="resources/images/flags/{{d.country}}.gif" /> {{d.city || '-'}}, {{d.country}}</td>
|
|
||||||
<td class="col-1 text-right">{{d.numberOfDuplicates}}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal fade" id="duplicatesModal" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog modal-xl" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">{{currentOrg.name}}</h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="text-muted" ng-if="currentDuplicates.length == 0">No duplicates</div>
|
|
||||||
<org-details org="currentOrgDetails" org-title="Registered organization" show="default"></org-details>
|
|
||||||
<org-form-duplicates duplicates="currentDuplicates"></org-form-duplicates>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
||||||
<button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="saveCurrentDuplicates()" ng-if="currentDuplicates.length > 0">Save changes</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-header bg-primary text-white py-1">Conflicts</div>
|
|
||||||
|
|
||||||
<div class="card-body text-muted" ng-if="conflicts.length == 0">No conflicts</div>
|
|
||||||
|
|
||||||
<table class="table table-sm mt-2" ng-if="conflicts.length > 0">
|
|
||||||
<thead>
|
|
||||||
<tr class="d-flex">
|
|
||||||
<th class="col-6 pl-3" style="border-top: none;">Related organization</th>
|
|
||||||
<th class="col-2 text-center" style="border-top: none;">Type</th>
|
|
||||||
<th class="col-4 text-right" style="border-top: none;">Place</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="d-flex" ng-repeat="sr in conflicts">
|
|
||||||
<td class="col-6 pl-3">{{sr.name}}<br /><a href="#!/edit/0/{{sr.id}}" class="small text-monospace">{{sr.id}}</a></td>
|
|
||||||
<td class="col-2 text-center small">{{sr.type}}</td>
|
|
||||||
<td class="col-4 text-right small"><img ng-src="resources/images/flags/{{sr.country}}.gif" /> {{sr.city || '-'}}, {{sr.country}}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="card-footer text-right" ng-if="showSaveButton && conflicts.length > 0">
|
|
||||||
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#resolveConflictsModal" ng-click="prepareConflictsModal()">resolve conflicts</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<resolve-conflicts-modal modal-id="resolveConflictsModal" orgs="candidateConflicts" selected-orgs="selectedConflicts"></resolve-conflicts-modal>
|
|
|
@ -1,297 +0,0 @@
|
||||||
<div class="card-body">
|
|
||||||
<form name="organizationForm">
|
|
||||||
|
|
||||||
<div ng-if="mode == 'approve'" class="alert alert-warning">
|
|
||||||
This is a pending organization. Please evaluate it before approving.
|
|
||||||
</div>
|
|
||||||
<div ng-if="mode == 'readonly'" class="alert alert-secondary">
|
|
||||||
This organization is managed by the system. You can not edit.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<fieldset ng-disabled="mode == 'readonly'">
|
|
||||||
<legend>Official name and type</legend>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">name</div>
|
|
||||||
</div>
|
|
||||||
<input name="org_nm" type="text" class="form-control"
|
|
||||||
placeholder="organization name..." ng-model="org.name"
|
|
||||||
required="required"
|
|
||||||
ng-class="{'is-invalid' : organizationForm.org_nm.$error.required}" />
|
|
||||||
<div class="input-group-append input-group-prepend">
|
|
||||||
<div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">type</div>
|
|
||||||
</div>
|
|
||||||
<select name="org_tp" class="custom-select" ng-model="org.type"
|
|
||||||
required="required"
|
|
||||||
ng-class="{'is-invalid' : organizationForm.org_tp.$error.required}">
|
|
||||||
<option disabled="disabled" value='' ng-if="!org.type">type...</option>
|
|
||||||
<option ng-repeat="t in vocabularies.orgTypes" value="{{t.value}}">{{t.name}}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="mt-4" ng-disabled="mode == 'readonly'">
|
|
||||||
<legend>Geographical location</legend>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">city</div>
|
|
||||||
</div>
|
|
||||||
<input name="org_ct" type="text" class="form-control" placeholder=""
|
|
||||||
ng-model="org.city" />
|
|
||||||
<div class="input-group-append input-group-prepend">
|
|
||||||
<div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">country</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select name="org_cntr" class="custom-select" ng-model="org.country"
|
|
||||||
required="required"
|
|
||||||
ng-class="{'is-invalid' : organizationForm.org_cntr.$error.required}">
|
|
||||||
<option disabled="disabled" value='' ng-if="!org.country">country...</option>
|
|
||||||
<option ng-repeat="c in vocabularies.countries"value="{{c.value}}">{{c.name}}</option>
|
|
||||||
</select>
|
|
||||||
<div class="input-group-append input-group-prepend">
|
|
||||||
<div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">lat</div>
|
|
||||||
</div>
|
|
||||||
<input name="org_lat" type="text" class="form-control"
|
|
||||||
placeholder="0.0" ng-model="org.lat" />
|
|
||||||
<div class="input-group-append input-group-prepend">
|
|
||||||
<div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">lng</div>
|
|
||||||
</div>
|
|
||||||
<input name="org_lng" type="text" class="form-control"
|
|
||||||
placeholder="0.0" ng-model="org.lng" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="mt-4" ng-disabled="mode == 'readonly'">
|
|
||||||
<legend>Other names and identifiers</legend>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-lg-8 mb-2">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Acronyms</div>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="a in org.acronyms">
|
|
||||||
<td>{{a}}</td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
|
||||||
ng-click="org.acronyms.splice($index, 1)">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr ng-init="newAcronym=''">
|
|
||||||
<td><input type="text" class="form-control form-control-sm"
|
|
||||||
placeholder="new acronym..." ng-model='newAcronym' /></td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-success"
|
|
||||||
ng-click="org.acronyms.push(newAcronym); newAcronym=''"
|
|
||||||
ng-disabled="!newAcronym || org.acronyms.indexOf(newAcronym) !== -1">
|
|
||||||
<i class="fa fa-plus"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-lg-8 mb-2">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Aliases</div>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr>
|
|
||||||
<th>name</th>
|
|
||||||
<th style="width: 156px">language</th>
|
|
||||||
<th style="width: 44px"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="n in org.otherNames">
|
|
||||||
<td>{{n.name}}</td>
|
|
||||||
<td>{{n.lang}}</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
|
||||||
ng-click="org.otherNames.splice($index, 1)">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr ng-init="newOtherName=newLang=''">
|
|
||||||
<td><input type="text" class="form-control form-control-sm"
|
|
||||||
placeholder="new alias..." ng-model="newOtherName" /></td>
|
|
||||||
<td><select class="custom-select custom-select-sm"
|
|
||||||
ng-model="newLang">
|
|
||||||
<option disabled="disabled" value=''>language...</option>
|
|
||||||
<option ng-repeat="l in vocabularies.languages" value="{{l.value}}">{{l.name}}</option>
|
|
||||||
</select></td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-success"
|
|
||||||
ng-click="org.otherNames.push({'name': newOtherName, 'lang': newLang}); newOtherName=newLang=''"
|
|
||||||
ng-disabled="!newOtherName || !newLang">
|
|
||||||
<i class="fa fa-plus"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-lg-8 mb-2">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Identifiers</div>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr>
|
|
||||||
<th>id</th>
|
|
||||||
<th style="width: 156px">type</th>
|
|
||||||
<th style="width: 44px"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="id in org.otherIdentifiers">
|
|
||||||
<td>{{id.id}}</td>
|
|
||||||
<td>{{id.type}}</td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
|
||||||
ng-click="org.otherIdentifiers.splice($index, 1)">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr ng-init="newId=newIdType=''">
|
|
||||||
<td><input type="text" class="form-control form-control-sm"
|
|
||||||
placeholder="new id..." ng-model="newId" /></td>
|
|
||||||
<td><select class="custom-select custom-select-sm"
|
|
||||||
ng-model="newIdType">
|
|
||||||
<option disabled="disabled" value=''>type...</option>
|
|
||||||
<option ng-repeat="t in vocabularies.idTypes" value="{{t.value}}">{{t.name}}</option>
|
|
||||||
</select></td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-success"
|
|
||||||
ng-click="org.otherIdentifiers.push({'id': newId, 'type': newIdType}); newId=newIdType=''"
|
|
||||||
ng-disabled="!newId || !newIdType">
|
|
||||||
<i class="fa fa-plus"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-lg-8 mb-2">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Urls</div>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="u in org.urls">
|
|
||||||
<td><a href="{{u}}" target="_blank">{{u}}</a></td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
|
||||||
ng-click="org.urls.splice($index, 1)">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr ng-init="newUrl=''">
|
|
||||||
<td><input type="text" class="form-control form-control-sm"
|
|
||||||
placeholder="http://..." ng-model="newUrl" /></td>
|
|
||||||
<td class="text-right" style="width: 44px">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-success"
|
|
||||||
ng-click="org.urls.push(newUrl); newUrl=''"
|
|
||||||
ng-disabled="!newUrl || org.urls.indexOf(newUrl) !== -1">
|
|
||||||
<i class="fa fa-plus"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="mt-4" ng-disabled="mode == 'readonly'">
|
|
||||||
<legend>Relations</legend>
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-lg-8 mb-2">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Relations</div>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr>
|
|
||||||
<th class="pl-3" style="border-top: none;">Related organization</th>
|
|
||||||
<th style="border-top: none; width: 156px">Type</th>
|
|
||||||
<th style="border-top: none; width: 44px"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-if="org.relations.length == 0">
|
|
||||||
<td class="text-center text-muted" colspan="3">No relations</td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat="r in org.relations">
|
|
||||||
<td class="pl-3"><a href="#!/edit/0/{{r.relatedOrgId}}" title="{{r.relatedOrgId}}">{{r.relatedOrgName}}</a></td>
|
|
||||||
<td>{{r.type}}</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger" ng-click="org.relations.splice($index, 1)"><i class="fa fa-trash"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<input type="text" placeholder="add a related organization..." readonly="readonly"
|
|
||||||
class="form-control form-control-sm" style="background-color: white; color: #007bff;"
|
|
||||||
ng-model="newRelation.name" ng-click="resetSelectedRelation()"
|
|
||||||
data-toggle="modal" data-target="#selectRelatedOrgModal" ng-hide="mode == 'readonly'"/>
|
|
||||||
<input type="text" placeholder="add a related organization..." disabled="disabled"
|
|
||||||
class="form-control form-control-sm" ng-model="newRelation.name" ng-show="mode == 'readonly'"/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select class="custom-select custom-select-sm" ng-model="newRelType">
|
|
||||||
<option disabled="disabled" value=''>rel type...</option>
|
|
||||||
<option ng-repeat="t in vocabularies.relTypes" value="{{t.value}}">{{t.name}}</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-success" ng-disabled="!newRelType || !newRelation.id" ng-click="addNewRelation()"><i class="fa fa-plus"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<button type="submit" class="btn" ng-class="{'btn-primary' : mode != 'approve', 'btn-warning' : mode == 'approve'}" ng-click="save()" ng-disabled="organizationForm.$invalid" ng-hide="mode == 'readonly'">
|
|
||||||
<span ng-if="mode != 'approve'">Save</span>
|
|
||||||
<span ng-if="mode == 'approve'">Register as new Organization</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select-org-modal modal-id="selectRelatedOrgModal" selected-org="newRelation"></select-org-modal>
|
|
|
@ -1,43 +0,0 @@
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="text-muted" ng-if="orgs.length == 0">No suggestions</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-3" ng-show="orgs.length > 0">
|
|
||||||
<input type="text" class="form-control" ng-model="orgFilter" placeholder="Filter..."/>
|
|
||||||
<div class="input-group-append">
|
|
||||||
<span class="input-group-text text-outline-primary">Country:</span>
|
|
||||||
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
|
|
||||||
<div class="dropdown-menu">
|
|
||||||
<small>
|
|
||||||
<a class="dropdown-item" href="#!/suggestions/{{c}}/0"
|
|
||||||
ng-repeat="(c, vals) in info.byCountry"
|
|
||||||
ng-if="vals.nPendingOrgs > 0">
|
|
||||||
{{c}} <span class="badge badge-primary float-right">{{vals.nPendingOrgs}}</span>
|
|
||||||
</a>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="table table-sm table-hover" ng-if="orgs.length > 0">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr class="d-flex">
|
|
||||||
<th class="col-6">Organization name</th>
|
|
||||||
<th class="col-4">Place</th>
|
|
||||||
<th class="col-1 text-center">Acronyms</th>
|
|
||||||
<th class="col-1 text-right">Type</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="o in orgs | filter:orgFilter" class="d-flex">
|
|
||||||
<td class="col-6">
|
|
||||||
<a href="#!/edit/0/{{o.id}}" title="{{o.id}}">{{o.name}}</a>
|
|
||||||
</td>
|
|
||||||
<td class="col-4"><img ng-src="resources/images/flags/{{o.country}}.gif" /> {{o.city || '-'}}, {{o.country}}</td>
|
|
||||||
<td class="col-1 text-center">{{o.acronyms.join()}}</td>
|
|
||||||
<td class="col-1 text-right">{{o.type}}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<div class="card-header">
|
|
||||||
<ul class="nav nav-tabs card-header-tabs">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="javascript:void(0)" class="nav-link" ng-class="{'active': selected == 1}" ng-click="loadOrg()">Metadata Management</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="javascript:void(0)" class="nav-link" ng-class="{'active': selected == 2}" ng-click="loadDedupEvents()">Dedup Events <span class="badge badge-primary ml-2">{{info.nDuplicates}}</span> <span class="badge badge-danger">{{info.nConflicts}}</span></a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<ul class="nav nav-tabs card-header-tabs">
|
|
||||||
<li class="nav-item"><a href="javascript:void(0)" class="nav-link active">Metadata</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<org-form-metadata org="org" vocabularies="vocabularies" mode="insert"></org-form-metadata>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<h2>Conflicts</h2>
|
||||||
|
|
||||||
|
<h4 class="text-muted" ng-if="conflicts.length == 0">No suggestions</h4>
|
||||||
|
|
||||||
|
<div class="input-group input-group-sm mb-3" ng-show="conflicts.length > 0">
|
||||||
|
<input type="text" class="form-control" ng-model="conflictFilter" placeholder="Filter...">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text text-outline-primary">Country:</span>
|
||||||
|
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<small>
|
||||||
|
<a class="dropdown-item" href="#!/conflicts/{{c}}"
|
||||||
|
ng-repeat="(c, vals) in info.data.byCountry"
|
||||||
|
ng-if="vals.nConflicts > 0">
|
||||||
|
{{c}} <span class="badge badge-danger float-right">{{vals.nConflicts}}</span>
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card text-white mb-3" ng-repeat="w in conflicts | filter:conflictFilter" class="mb-2">
|
||||||
|
<div class="card-header bg-primary text-white py-1">Group {{$index+1}}</div>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tr ng-repeat="o in w">
|
||||||
|
<th style="width:40px" class="text-center">#{{$index+1}}</th>
|
||||||
|
<td><a href="#!/edit/0/{{o.id}}" title="{{o.id}}">{{o.name}}</a></td>
|
||||||
|
<td style="width:250px"><img ng-src="resources/images/flags/{{o.country}}.gif" /> {{o.city || '-'}}, {{o.country}}</td>
|
||||||
|
<td style="width:80px" class="text-right">{{o.type}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="card-footer bg-secondary py-1">
|
||||||
|
<button class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#addNewConflictModal" ng-click="prepareAddConflictModal(w)">add</button>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-primary"
|
||||||
|
data-toggle="modal" data-target="#resolveConflictsModal"
|
||||||
|
ng-click="prepareConflictsModal(w)">resolve</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<resolve-conflicts-modal modal-id="resolveConflictsModal" orgs="orgs" selected-orgs="selectedOrgs"></resolve-conflicts-modal>
|
||||||
|
|
||||||
|
<select-org-modal modal-id="addNewConflictModal" selected-org="newConflict" filter-status="approved" on-select="addConflict()"></select-org-modal>
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
<h2>Duplicates</h2>
|
||||||
|
|
||||||
|
<h4 class="text-muted" ng-if="duplicates.length == 0">No duplicates</h4>
|
||||||
|
|
||||||
|
<div class="input-group input-group-sm mb-3" ng-show="duplicates.length > 0">
|
||||||
|
<input type="text" class="form-control" ng-model="duplicateFilter" placeholder="Filter...">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text text-outline-primary">Country:</span>
|
||||||
|
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<small>
|
||||||
|
<a class="dropdown-item" href="#!/duplicates/{{c}}"
|
||||||
|
ng-repeat="(c, vals) in info.data.byCountry"
|
||||||
|
ng-if="vals.nDuplicates > 0">
|
||||||
|
{{c}} <span class="badge badge-primary float-right">{{vals.nDuplicates}}</span>
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-sm table-hover" ng-if="duplicates.length > 0">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr class="d-flex">
|
||||||
|
<th class="col-8">Organization</th>
|
||||||
|
<th class="col-3">Place</th>
|
||||||
|
<th class="col-1 text-right"># pending duplicates</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="d in duplicates | filter:duplicateFilter" class="d-flex">
|
||||||
|
<td class="col-8">
|
||||||
|
<a href="javascript:void(0)" title="{{d.id}}" ng-click="prepareDuplicatesModal(d)" data-toggle="modal" data-target="#duplicatesModal">{{d.name}}</a>
|
||||||
|
<a href="#!/edit/0/{{d.id}}" title="edit"><i class="fa fa-edit"></i></a>
|
||||||
|
</td>
|
||||||
|
<td class="col-3"><img ng-src="resources/images/flags/{{d.country}}.gif" /> {{d.city || '-'}}, {{d.country}}</td>
|
||||||
|
<td class="col-1 text-right">{{d.numberOfDuplicates}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="modal fade" id="duplicatesModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog modal-xl" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">{{currentOrg.name}}</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="text-muted" ng-if="currentDuplicates.length == 0">No duplicates</div>
|
||||||
|
<org-details org="currentOrgDetails" org-title="Registered organization" show="default"></org-details>
|
||||||
|
<org-duplicates org-id="currentOrgDetails.id" duplicates="currentDuplicates"></org-form-duplicates>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="saveCurrentDuplicates()" ng-if="currentDuplicates.length > 0">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<ul class="nav nav-tabs card-header-tabs">
|
||||||
|
<li class="nav-item"><a href="javascript:void(0)" class="nav-link active">Metadata</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" mode="insert_full" ng-if="superAdmin"></org-form-metadata>
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" mode="insert_pending" ng-if="!superAdmin"></org-form-metadata>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,42 @@
|
||||||
|
<h2>Pending Organizations</h2>
|
||||||
|
|
||||||
|
<h4 class="text-muted" ng-if="orgs.length == 0">No pending organizations</h4>
|
||||||
|
|
||||||
|
<div class="input-group input-group-sm mb-3" ng-show="orgs.length > 0">
|
||||||
|
<input type="text" class="form-control" ng-model="orgFilter" placeholder="Filter..."/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text text-outline-primary">Country:</span>
|
||||||
|
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<small>
|
||||||
|
<a class="dropdown-item" href="#!/pendings/{{c}}"
|
||||||
|
ng-repeat="(c, vals) in info.data.byCountry"
|
||||||
|
ng-if="vals.nPendingOrgs > 0">
|
||||||
|
{{c}} <span class="badge badge-primary float-right">{{vals.nPendingOrgs}}</span>
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-sm table-hover" ng-if="orgs.length > 0">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr class="d-flex">
|
||||||
|
<th class="col-6">Organization name</th>
|
||||||
|
<th class="col-4">Place</th>
|
||||||
|
<th class="col-1 text-center">Acronyms</th>
|
||||||
|
<th class="col-1 text-right">Type</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="o in orgs | filter:orgFilter" class="d-flex">
|
||||||
|
<td class="col-6">
|
||||||
|
<a href="#!/edit/0/{{o.id}}" title="{{o.id}}">{{o.name}}</a>
|
||||||
|
</td>
|
||||||
|
<td class="col-4"><img ng-src="resources/images/flags/{{o.country}}.gif" /> {{o.city || '-'}}, {{o.country}}</td>
|
||||||
|
<td class="col-1 text-center">{{o.acronyms.join()}}</td>
|
||||||
|
<td class="col-1 text-right">{{o.type}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<h4>{{info.name}}</h4>
|
||||||
|
|
||||||
|
<div class="alert alert-success" ng-if="message">{{message}}</div>
|
||||||
|
|
||||||
|
<p class="text-muted" ng-if="!message">
|
||||||
|
<b>ID: </b>{{info.id}}<br /> <b>Created at</b> {{info.creationDate | date:'MMMM d, y HH:mm:ss'}} <b>by</b> {{info.createdBy}}<br /> <b>Modified at</b> {{info.modificationDate | date:'MMMM d, y HH:mm:ss'}} <b>by</b>
|
||||||
|
{{info.modifiedBy}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<ul class="nav nav-tabs card-header-tabs">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="javascript:void(0)" class="nav-link" ng-class="{'active': currentTab == 1}" ng-click="gotoTab(1)">Metadata Management</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" ng-show="org.status == 'approved'">
|
||||||
|
<a href="javascript:void(0)" class="nav-link" ng-class="{'active': currentTab == 2}" ng-click="gotoTab(2)">Duplicates <span class="badge badge-danger ml-2" ng-if="info.nDuplicates > 0" title="{{info.nDuplicates}} duplicate(s) to validate">new</span></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" ng-show="org.status == 'approved' && adminMode">
|
||||||
|
<a href="javascript:void(0)" class="nav-link" ng-class="{'active': currentTab == 3}" ng-click="gotoTab(3)">Conflicts <span class="badge badge-danger ml-2" ng-if="info.nConflicts > 0" title="the current organization seems in conflict with {{info.nConflicts}} other(s)">new</span></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body" ng-if="currentTab == 1 && org.status">
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" info-method="getInfo()" mode="update_full" ng-if="adminMode && org.status == 'approved'"></org-form-metadata>
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" info-method="getInfo()" mode="update_simple" ng-if="!adminMode && org.status == 'approved'"></org-form-metadata>
|
||||||
|
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" info-method="getInfo()" mode="approve" ng-if="adminMode && org.status == 'suggested'"></org-form-metadata>
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" info-method="getInfo()" mode="not_authorized" ng-if="!adminMode && org.status == 'suggested'"></org-form-metadata>
|
||||||
|
|
||||||
|
<org-form-metadata org="org" vocabularies="vocabularies" info-method="getInfo()" mode="readonly" ng-if="org.status != 'approved' && org.status != 'suggested'"></org-form-metadata>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body" ng-if="currentTab == 2 && (org.status == 'approved' || org.status == 'suggested')">
|
||||||
|
<org-details org="org" org-title="Current organization" show="default"></org-details>
|
||||||
|
<org-duplicates org-id="{{org.id}}" duplicates="duplicates" save-function="saveDuplicates()" show-save-button="1"></org-form-duplicates>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body" ng-if="adminMode && currentTab == 3 && (org.status == 'approved' || org.status == 'suggested')">
|
||||||
|
<org-conflicts org="org" conflicts="conflicts" show-save-button="1"></org-form-conflicts>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,50 @@
|
||||||
|
<h2>{{title}}</h2>
|
||||||
|
|
||||||
|
<h4 class="text-muted" ng-if="entries.length == 0">No entries</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<input type="text" class="form-control col-lg-8 col-xs-12" ng-show="entries.length > 0" ng-model="browseFilter" placeholder="Filter...">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table class="table table-sm table-bordered table-hover col-lg-8 col-xs-12">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th class="col-6">{{field}}</th>
|
||||||
|
<th class="col-1 text-right text-nowrap" title="valid organizations"># approved</th>
|
||||||
|
<th class="col-1 text-right text-nowrap" title="to be approved by an administrator"># suggested</th>
|
||||||
|
<th class="col-1 text-right text-nowrap" title="deleted by an administrator"># deleted</th>
|
||||||
|
<th class="col-1 text-right text-nowrap" title="duplicates(not openorgs) of a valid organization"># duplicated</th>
|
||||||
|
<th class="col-1 text-right text-nowrap" title="suggestions rejected by an administrator"># discarded</th>
|
||||||
|
<th class="col-1 text-right text-nowrap" title="organizations hidden by the system (for example fixing a conflict)"># hidden</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="e in entries | filter: browseFilter">
|
||||||
|
<th>{{e.name}} <span ng-if="e.code != e.name"> ({{e.code}})</span></th>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#!{{resultsBasePath}}/0/50/approved/{{e.code}}" ng-if="e.values.approved && e.values.approved > 0">{{e.values.approved}}</a>
|
||||||
|
<span ng-if="!e.values.approved || e.values.approved == 0">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#!{{resultsBasePath}}/0/50/suggested/{{e.code}}" ng-if="e.values.suggested && e.values.suggested > 0">{{e.values.suggested}}</a>
|
||||||
|
<span ng-if="!e.values.suggested || e.values.suggested == 0">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#!{{resultsBasePath}}/0/50/deleted/{{e.code}}" ng-if="e.values.deleted && e.values.deleted > 0">{{e.values.deleted}}</a>
|
||||||
|
<span ng-if="!e.values.deleted || e.values.deleted == 0">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#!{{resultsBasePath}}/0/50/duplicate/{{e.code}}" ng-if="e.values.duplicate && e.values.duplicate > 0">{{e.values.duplicate}}</a>
|
||||||
|
<span ng-if="!e.values.duplicate || e.values.duplicate == 0">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#!{{resultsBasePath}}/0/50/discarded/{{e.code}}" ng-if="e.values.discarded && e.values.discarded > 0">{{e.values.discarded}}</a>
|
||||||
|
<span ng-if="!e.values.discarded || e.values.discarded == 0">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#!{{resultsBasePath}}/0/50/hidden/{{e.code}}" ng-if="e.values.hidden && e.values.hidden > 0">{{e.values.hidden}}</a>
|
||||||
|
<span ng-if="!e.values.hidden || e.values.hidden == 0">-</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<div class="card text-white mb-3">
|
||||||
|
<div class="card-header bg-primary text-white py-1">Conflicts</div>
|
||||||
|
|
||||||
|
<div class="card-body text-muted" ng-if="conflicts.length == 0">No conflicts</div>
|
||||||
|
|
||||||
|
<table class="table table-sm" ng-if="conflicts.length > 0">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th style="width:40px" class="text-center">#1</th>
|
||||||
|
<td><a href="javascript:location.reload()" title="{{org.id}}">{{org.name}}</a> <span class="badge badge-primary">current</span></td>
|
||||||
|
<td style="width:250px"><img ng-src="resources/images/flags/{{org.country}}.gif" /> {{org.city || '-'}}, {{org.country}}</td>
|
||||||
|
<td style="width:80px" class="text-right">{{org.type}}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr ng-repeat="sr in conflicts">
|
||||||
|
<th style="width:40px" class="text-center">#{{$index+2}}</th>
|
||||||
|
<td><a href="#!/edit/0/{{sr.id}}" title="{{sr.id}}">{{sr.name}}</a></td>
|
||||||
|
<td style="width:250px"><img ng-src="resources/images/flags/{{sr.country}}.gif" /> {{sr.city || '-'}}, {{sr.country}}</td>
|
||||||
|
<td style="width:80px" class="text-right">{{sr.type}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="card-footer bg-secondary py-1">
|
||||||
|
<button class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#addConflictModal">add</button>
|
||||||
|
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#resolveConflictsModal" ng-click="prepareConflictsModal()" ng-if="showSaveButton && conflicts.length > 0">resolve conflicts</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<resolve-conflicts-modal modal-id="resolveConflictsModal" orgs="candidateConflicts" selected-orgs="selectedConflicts"></resolve-conflicts-modal>
|
||||||
|
|
||||||
|
<select-org-modal modal-id="addConflictModal" selected-org="newConflict" filter-status="approved" on-select="addConflict()"></select-org-modal>
|
|
@ -1,11 +0,0 @@
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<org-details org="currentOrgDetails" org-title="Current organization" show="default"></org-details>
|
|
||||||
|
|
||||||
<org-form-duplicates duplicates="events.duplicates" save-function="saveDuplicates()" show-save-button="1"></org-form-duplicates>
|
|
||||||
|
|
||||||
<org-form-conflicts conflicts="events.conflicts" save-function="saveConflicts()" org-id="{{currentOrgDetails.id}}" show-save-button="1"></org-form-conflicts>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
'text-white bg-info' : show == 'info',
|
'text-white bg-info' : show == 'info',
|
||||||
'bg-secondary' : show == 'secondary',
|
'bg-secondary' : show == 'secondary',
|
||||||
}">{{orgTitle}}</div>
|
}">{{orgTitle}}</div>
|
||||||
<table class="table table-sm table-condensed">
|
<table class="table table-sm table-condensed" style="table-layout: fixed">
|
||||||
<tr class="d-flex">
|
<tr class="d-flex">
|
||||||
<th class="col-4 pl-3">Name</th>
|
<th class="col-4 pl-3">Name</th>
|
||||||
<td class="col-8">{{org.name}}</td>
|
<td class="col-8">{{org.name}}</td>
|
||||||
|
|
|
@ -38,8 +38,11 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="card-footer text-right" ng-if="showSaveButton && duplicates.length > 0">
|
<div class="card-footer text-right">
|
||||||
<button class="btn btn-sm btn-primary" ng-click="saveFunction()">save</button>
|
<button class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#addDuplicateModal">add</button>
|
||||||
|
<button class="btn btn-sm btn-primary" ng-click="saveFunction()" ng-if="showSaveButton && duplicates.length > 0">save</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<select-org-modal modal-id="addDuplicateModal" selected-org="newDuplicate" filter-status="suggested,discarded" on-select="addDuplicate()"></select-org-modal>
|
|
@ -0,0 +1,312 @@
|
||||||
|
<form name="organizationForm">
|
||||||
|
|
||||||
|
<div ng-if="mode == 'approve'" class="alert alert-warning">
|
||||||
|
This is a pending organization. Please evaluate it before approving.
|
||||||
|
</div>
|
||||||
|
<div ng-if="mode == 'readonly'" class="alert alert-secondary">
|
||||||
|
<span class="badge badge-primary">{{org.status}}</span> This organization is managed by the system. You can not edit.
|
||||||
|
</div>
|
||||||
|
<div ng-if="mode == 'not_authorized'" class="alert alert-secondary">
|
||||||
|
<span class="badge badge-primary">{{org.status}}</span> You are not authorized to modify this organization.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset ng-disabled="mode == 'readonly' || mode == 'not_authorized'">
|
||||||
|
<legend>Official name and type</legend>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text bg-primary text-white" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">name</div>
|
||||||
|
</div>
|
||||||
|
<input name="org_nm" type="text" class="form-control"
|
||||||
|
placeholder="organization name..." ng-model="org.name"
|
||||||
|
required="required"
|
||||||
|
ng-class="{'is-invalid' : organizationForm.org_nm.$error.required}" />
|
||||||
|
<div class="input-group-append input-group-prepend">
|
||||||
|
<div class="input-group-text bg-primary text-white" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">type</div>
|
||||||
|
</div>
|
||||||
|
<select name="org_tp" class="custom-select" ng-model="org.type"
|
||||||
|
required="required"
|
||||||
|
ng-class="{'is-invalid' : organizationForm.org_tp.$error.required}">
|
||||||
|
<option disabled="disabled" value='' ng-if="!org.type">type...</option>
|
||||||
|
<option ng-repeat="t in vocabularies.orgTypes" value="{{t.value}}">{{t.name}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="mt-4" ng-disabled="mode == 'readonly' || mode == 'not_authorized'">
|
||||||
|
<legend>Geographical location</legend>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text bg-primary text-white" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">city</div>
|
||||||
|
</div>
|
||||||
|
<input name="org_ct" type="text" class="form-control" placeholder=""
|
||||||
|
ng-model="org.city" />
|
||||||
|
<div class="input-group-append input-group-prepend">
|
||||||
|
<div class="input-group-text bg-primary text-white" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">country</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<select name="org_cntr" class="custom-select" ng-model="org.country"
|
||||||
|
required="required"
|
||||||
|
ng-class="{'is-invalid' : organizationForm.org_cntr.$error.required}">
|
||||||
|
<option disabled="disabled" value='' ng-if="!org.country">country...</option>
|
||||||
|
<option ng-repeat="c in vocabularies.countries"value="{{c.value}}">{{c.name}}</option>
|
||||||
|
</select>
|
||||||
|
<div class="input-group-append input-group-prepend">
|
||||||
|
<div class="input-group-text bg-primary text-white" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">lat</div>
|
||||||
|
</div>
|
||||||
|
<input name="org_lat" type="text" class="form-control"
|
||||||
|
placeholder="0.0" ng-model="org.lat" />
|
||||||
|
<div class="input-group-append input-group-prepend">
|
||||||
|
<div class="input-group-text bg-primary text-white" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">lng</div>
|
||||||
|
</div>
|
||||||
|
<input name="org_lng" type="text" class="form-control"
|
||||||
|
placeholder="0.0" ng-model="org.lng" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="mt-4" ng-disabled="mode == 'readonly' || mode == 'not_authorized'">
|
||||||
|
<legend>Other names and identifiers</legend>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-lg-8 mb-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white text-center py-1 bg-primary" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">Acronyms</div>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="a in org.acronyms">
|
||||||
|
<td>{{a}}</td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||||
|
ng-click="org.acronyms.splice($index, 1)">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr ng-init="newAcronym=''">
|
||||||
|
<td><input type="text" class="form-control form-control-sm"
|
||||||
|
placeholder="new acronym..." ng-model='newAcronym' /></td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-success"
|
||||||
|
ng-click="org.acronyms.push(newAcronym); newAcronym=''"
|
||||||
|
ng-disabled="!newAcronym || org.acronyms.indexOf(newAcronym) !== -1">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-lg-8 mb-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white text-center py-1 bg-primary" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">Aliases</div>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th>name</th>
|
||||||
|
<th style="width: 156px">language</th>
|
||||||
|
<th style="width: 44px"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="n in org.otherNames">
|
||||||
|
<td>{{n.name}}</td>
|
||||||
|
<td>{{n.lang}}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||||
|
ng-click="org.otherNames.splice($index, 1)">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr ng-init="newOtherName=newLang=''">
|
||||||
|
<td><input type="text" class="form-control form-control-sm"
|
||||||
|
placeholder="new alias..." ng-model="newOtherName" /></td>
|
||||||
|
<td><select class="custom-select custom-select-sm"
|
||||||
|
ng-model="newLang">
|
||||||
|
<option disabled="disabled" value=''>language...</option>
|
||||||
|
<option ng-repeat="l in vocabularies.languages" value="{{l.value}}">{{l.name}}</option>
|
||||||
|
</select></td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-success"
|
||||||
|
ng-click="org.otherNames.push({'name': newOtherName, 'lang': newLang}); newOtherName=newLang=''"
|
||||||
|
ng-disabled="!newOtherName || !newLang">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-lg-8 mb-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white text-center py-1 bg-primary" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">Identifiers</div>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th>id</th>
|
||||||
|
<th style="width: 156px">type</th>
|
||||||
|
<th style="width: 44px"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="id in org.otherIdentifiers">
|
||||||
|
<td>{{id.id}}</td>
|
||||||
|
<td>{{id.type}}</td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||||
|
ng-click="org.otherIdentifiers.splice($index, 1)">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr ng-init="newId=newIdType=''">
|
||||||
|
<td><input type="text" class="form-control form-control-sm"
|
||||||
|
placeholder="new id..." ng-model="newId" /></td>
|
||||||
|
<td><select class="custom-select custom-select-sm"
|
||||||
|
ng-model="newIdType">
|
||||||
|
<option disabled="disabled" value=''>type...</option>
|
||||||
|
<option ng-repeat="t in vocabularies.idTypes" value="{{t.value}}">{{t.name}}</option>
|
||||||
|
</select></td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-success"
|
||||||
|
ng-click="org.otherIdentifiers.push({'id': newId, 'type': newIdType}); newId=newIdType=''"
|
||||||
|
ng-disabled="!newId || !newIdType">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-lg-8 mb-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white text-center py-1 bg-primary" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">Urls</div>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="u in org.urls">
|
||||||
|
<td><a href="{{u}}" target="_blank">{{u}}</a></td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||||
|
ng-click="org.urls.splice($index, 1)">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr ng-init="newUrl=''">
|
||||||
|
<td><input type="text" class="form-control form-control-sm"
|
||||||
|
placeholder="http://..." ng-model="newUrl" /></td>
|
||||||
|
<td class="text-right" style="width: 44px">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-success"
|
||||||
|
ng-click="org.urls.push(newUrl); newUrl=''"
|
||||||
|
ng-disabled="!newUrl || org.urls.indexOf(newUrl) !== -1">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="mt-4" ng-disabled="mode == 'readonly' || mode == 'not_authorized'">
|
||||||
|
<legend>Relations</legend>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-lg-8 mb-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-white text-center py-1 bg-primary" ng-class="{'bg-light text-dark' : mode == 'readonly' || mode == 'not_authorized', 'bg-warning' : mode == 'approve'}">Relations</div>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th class="pl-3" style="border-top: none;">Related organization</th>
|
||||||
|
<th style="border-top: none; width: 156px">Type</th>
|
||||||
|
<th style="border-top: none; width: 44px"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-if="org.relations.length == 0">
|
||||||
|
<td class="text-center text-muted" colspan="3">No relations</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="r in org.relations">
|
||||||
|
<td class="pl-3"><a href="#!/edit/0/{{r.relatedOrgId}}" title="{{r.relatedOrgId}}">{{r.relatedOrgName}}</a></td>
|
||||||
|
<td>{{r.type}}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" ng-click="org.relations.splice($index, 1)"><i class="fa fa-trash"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="text" placeholder="add a related organization..." readonly="readonly"
|
||||||
|
class="form-control form-control-sm" style="background-color: white; color: #007bff;"
|
||||||
|
ng-model="newRelation.name" ng-click="resetSelectedRelation()"
|
||||||
|
data-toggle="modal" data-target="#selectRelatedOrgModal" ng-hide="mode == 'readonly'"/>
|
||||||
|
<input type="text" placeholder="add a related organization..." disabled="disabled"
|
||||||
|
class="form-control form-control-sm" ng-model="newRelation.name" ng-show="mode == 'readonly'"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select class="custom-select custom-select-sm" ng-model="newRelType">
|
||||||
|
<option disabled="disabled" value=''>rel type...</option>
|
||||||
|
<option ng-repeat="t in vocabularies.relTypes" value="{{t.value}}">{{t.name}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-success" ng-disabled="!newRelType || !newRelation.id" ng-click="addNewRelation()"><i class="fa fa-plus"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
|
||||||
|
<fieldset ng-if="mode == 'update_full'">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="organizationForm.$invalid">Save</button>
|
||||||
|
<button type="button" class="btn btn-danger" ng-click="deleteOrg()">Delete</button>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset ng-if="mode == 'update_simple'">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="organizationForm.$invalid">Save</button>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset ng-if="mode == 'approve'">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="organizationForm.$invalid">Approve as new Organization</button>
|
||||||
|
<button type="button" class="btn btn-danger" ng-click="saveAsDiscarded()">Discard</button>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset ng-if="mode == 'insert_full'">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="organizationForm.$invalid">Register a new Organization</button>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset ng-if="mode == 'insert_pending'">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="organizationForm.$invalid">Suggest a new Organization</button>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<select-org-modal modal-id="selectRelatedOrgModal" selected-org="newRelation" filter-status="approved"></select-org-modal>
|
|
@ -16,6 +16,7 @@
|
||||||
<org-results-page orgs="searchOrgs"
|
<org-results-page orgs="searchOrgs"
|
||||||
prev-function="search(searchValue, searchOrgs.number - 1, searchOrgs.size)"
|
prev-function="search(searchValue, searchOrgs.number - 1, searchOrgs.size)"
|
||||||
next-function="search(searchValue, searchOrgs.number + 1, searchOrgs.size)"
|
next-function="search(searchValue, searchOrgs.number + 1, searchOrgs.size)"
|
||||||
|
on-select="selectOrg()"
|
||||||
selected-org="selectedOrg"
|
selected-org="selectedOrg"
|
||||||
mode="select-modal"></org-results-page>
|
mode="select-modal"></org-results-page>
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<div class="card">
|
|
||||||
|
|
||||||
<div class="card-header">
|
|
||||||
<ul class="nav nav-tabs card-header-tabs">
|
|
||||||
<li class="nav-item text-nowrap">
|
|
||||||
<a href="#!/suggestions/_/0" class="nav-link" ng-class="{'active': currentTab == 0}">Pending Organizations <span class="badge badge-primary ml-2">{{info.total.nPendingOrgs}}</span></a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item text-nowrap">
|
|
||||||
<a href="#!/suggestions/_/1" class="nav-link" ng-class="{'active': currentTab == 1}">New Duplicates <span class="badge badge-primary ml-2">{{info.total.nDuplicates}}</span></a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item text-nowrap">
|
|
||||||
<a href="#!/suggestions/_/2" class="nav-link" ng-class="{'active': currentTab == 2}">New Conflicts <span class="badge badge-danger ml-2">{{info.total.nConflicts}}</span></a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
<pending-orgs orgs="pendingOrgs" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 0"></pending-orgs>
|
|
||||||
<all-duplicates duplicates="duplicates" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 1"></all-duplicates>
|
|
||||||
<all-conflicts conflicts="conflicts" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 2"></all-conflicts>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
|
@ -4,12 +4,9 @@ orgsModule.service('vocabulariesService', function($http) {
|
||||||
this.vocs = {};
|
this.vocs = {};
|
||||||
this.getVocs = function(f) {
|
this.getVocs = function(f) {
|
||||||
if (Object.keys(this.vocs).length === 0) {
|
if (Object.keys(this.vocs).length === 0) {
|
||||||
$http.get('api/vocabularies').then(function successCallback(res) {
|
call_http_get($http, 'api/vocabularies', function(res) {
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
this.vocs = res.data;
|
this.vocs = res.data;
|
||||||
f(angular.copy(this.vocs));
|
f(angular.copy(this.vocs));
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
f(this.vocs);
|
f(this.vocs);
|
||||||
|
@ -17,29 +14,66 @@ orgsModule.service('vocabulariesService', function($http) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.directive('selectOrgModal', function($http) {
|
orgsModule.factory('suggestionInfo', function($http) {
|
||||||
|
var info = { data : {} };
|
||||||
|
|
||||||
|
var getInfo = function() { return info; };
|
||||||
|
|
||||||
|
var updateInfo = function(callback) {
|
||||||
|
call_http_get($http, 'api/organizations/suggestionsInfo', function(res) {
|
||||||
|
info.data = res.data;
|
||||||
|
if (callback) { callback(info); }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getInfo: getInfo,
|
||||||
|
updateInfo: updateInfo
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
orgsModule.controller('menuCtrl', function ($scope, suggestionInfo) {
|
||||||
|
$scope.info = suggestionInfo.getInfo();
|
||||||
|
suggestionInfo.updateInfo(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
orgsModule.directive('selectOrgModal', function($http, $timeout) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
'modalId' : '@',
|
'modalId' : '@',
|
||||||
'selectedOrg' : '='
|
'filterStatus' : '@',
|
||||||
|
'selectedOrg' : '=',
|
||||||
|
'onSelect' : '&'
|
||||||
},
|
},
|
||||||
templateUrl: 'resources/html/modals/select_org.html',
|
templateUrl: 'resources/html/parts/select_org.modal.html',
|
||||||
link: function(scope, element, attrs, ctrl) {
|
link: function(scope, element, attrs, ctrl) {
|
||||||
scope.searchOrgs = {};
|
scope.searchOrgs = {};
|
||||||
scope.searchValue = '';
|
scope.searchText = '';
|
||||||
|
scope.searchValue = '';
|
||||||
|
|
||||||
scope.search = function(text, page, size) {
|
scope.search = function(text, page, size) {
|
||||||
scope.searchOrgs = {};
|
scope.searchOrgs = {};
|
||||||
|
|
||||||
$http.get('api/organizations/search/' + page + '/' + size + '?q=' + text).then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/search/' + page + '/' + size + '?status='+ scope.filterStatus + '&q=' + text, function(res) {
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.searchValue = text;
|
scope.searchValue = text;
|
||||||
scope.searchOrgs = res.data;
|
scope.searchOrgs = res.data;
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.selectOrg = function() {
|
||||||
|
$timeout(function() {
|
||||||
|
scope.searchText = '';
|
||||||
|
scope.searchValue = '';
|
||||||
|
scope.searchOrgs = {};
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
if (scope.onSelect) {
|
||||||
|
scope.onSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -52,7 +86,7 @@ orgsModule.directive('resolveConflictsModal', function($http, $route) {
|
||||||
'orgs' : '=',
|
'orgs' : '=',
|
||||||
'selectedOrgs' : '='
|
'selectedOrgs' : '='
|
||||||
},
|
},
|
||||||
templateUrl: 'resources/html/modals/resolve_conflicts.html',
|
templateUrl: 'resources/html/parts/resolve_conflicts.modal.html',
|
||||||
link: function(scope, element, attrs, ctrl) {
|
link: function(scope, element, attrs, ctrl) {
|
||||||
scope.selectOrg = function(org) {
|
scope.selectOrg = function(org) {
|
||||||
var sel = angular.copy(org);
|
var sel = angular.copy(org);
|
||||||
|
@ -75,15 +109,10 @@ orgsModule.directive('resolveConflictsModal', function($http, $route) {
|
||||||
else { otherIds.push(o.id); }
|
else { otherIds.push(o.id); }
|
||||||
});
|
});
|
||||||
if (masterId && otherIds.length > 0) {
|
if (masterId && otherIds.length > 0) {
|
||||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
|
||||||
$http.post('api/organizations/conflicts/fix/' + masterId, otherIds).then(function successCallback(res) {
|
call_http_post($http, 'api/organizations/conflicts/fix/' + masterId, otherIds, function(res) {
|
||||||
if ((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$('#' + scope.modalId).modal('hide');
|
$('#' + scope.modalId).modal('hide');
|
||||||
$('#' + scope.modalId).on('hidden.bs.modal', function (e) {
|
$('#' + scope.modalId).on('hidden.bs.modal', function (e) { $route.reload(); });
|
||||||
$route.reload();
|
|
||||||
});
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,67 +120,17 @@ orgsModule.directive('resolveConflictsModal', function($http, $route) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.directive('orgTabsMenu', function($http) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
'orgId' : '@',
|
|
||||||
'info' : '=',
|
|
||||||
'selected' : '=',
|
|
||||||
'org' : '=',
|
|
||||||
'events' : '=',
|
|
||||||
},
|
|
||||||
templateUrl: 'resources/html/menu/org_tabs_menu.html',
|
|
||||||
link: function(scope, element, attrs, ctrl) {
|
|
||||||
|
|
||||||
scope.loadOrg = function() {
|
|
||||||
scope.org = {};
|
|
||||||
$http.get('api/organizations/get?id=' + scope.orgId).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.org = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
scope.selected = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.loadDedupEvents = function() {
|
|
||||||
scope.events = {};
|
|
||||||
|
|
||||||
$http.get('api/organizations/conflicts?id=' + scope.orgId).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.events.conflicts = res.data;
|
|
||||||
$http.get('api/organizations/duplicates?id=' + scope.orgId).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.events.duplicates = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
scope.selected = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.loadOrg();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
orgsModule.directive('orgFormMetadata', function($http, $location, $route, $routeParams) {
|
orgsModule.directive('orgFormMetadata', function($http, $location, $route, $routeParams) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
'orgId' : '@',
|
|
||||||
'org' : '=',
|
'org' : '=',
|
||||||
'vocabularies' : '=',
|
'vocabularies' : '=',
|
||||||
'mode' : '@', // insert, update or approve
|
'mode' : '@', // insert_full, insert_pending, update_simple, update_full, approve, readonly, not_authorized
|
||||||
'infoMethod' : '&'
|
'infoMethod' : '&'
|
||||||
},
|
},
|
||||||
templateUrl: 'resources/html/forms/org_metadata.html',
|
templateUrl: 'resources/html/parts/org_metadata.form.html',
|
||||||
link: function(scope, element, attrs, ctrl) {
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
|
||||||
scope.newRelation = {};
|
scope.newRelation = {};
|
||||||
scope.newRelType = '';
|
scope.newRelType = '';
|
||||||
|
|
||||||
|
@ -170,62 +149,34 @@ orgsModule.directive('orgFormMetadata', function($http, $location, $route, $rout
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.save = function() {
|
scope.save = function() {
|
||||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
call_http_post($http, 'api/organizations/save', scope.org, function(res) {
|
||||||
$http.post('api/organizations/save', scope.org).then(function successCallback(res) {
|
if (scope.mode == 'insert') { $location.url('/edit/1/' + res.data[0]); }
|
||||||
if ((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
else if (scope.mode == 'insert') { $location.url('/edit/1/' + res.data[0]); }
|
|
||||||
else if (scope.mode == 'approve') { $location.url('/edit/3/' + res.data[0]); }
|
else if (scope.mode == 'approve') { $location.url('/edit/3/' + res.data[0]); }
|
||||||
else if ($routeParams.msg == 2) { $route.reload(); }
|
else if ($routeParams.msg == 2) { $route.reload(); }
|
||||||
else { $location.url('/edit/2/' + res.data[0]); }
|
else { $location.url('/edit/2/' + res.data[0]); }
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.deleteOrg = function() {
|
||||||
|
if (confirm("Are you sure?")) {
|
||||||
|
call_http_get($http, 'api/organizations/delete?id=' + scope.org.id, function(res) {
|
||||||
|
alert("Organization marked as deleted !!!");
|
||||||
|
$route.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
scope.saveAsDiscarded = function() {
|
||||||
|
if (confirm("Are you sure?")) {
|
||||||
|
call_http_get($http, 'api/organizations/discard?id=' + scope.org.id, function(res) {
|
||||||
|
alert("Organization marked as discarded !!!");
|
||||||
|
$route.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.directive('orgDedupEvents', function($http, $location, $route) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
'orgId' : '@',
|
|
||||||
'events' : '=',
|
|
||||||
'vocabularies' : '=',
|
|
||||||
'infoMethod' : '&'
|
|
||||||
},
|
|
||||||
templateUrl: 'resources/html/parts/org_dedup_events.html',
|
|
||||||
link: function(scope, element, attrs, ctrl) {
|
|
||||||
|
|
||||||
scope.currentOrgDetails = {};
|
|
||||||
|
|
||||||
$http.get('api/organizations/get?id=' + scope.orgId).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.currentOrgDetails = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.saveDuplicates = function() {
|
|
||||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
|
||||||
$http.post('api/organizations/duplicates', scope.events.duplicates).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
if (scope.infoMethod) { scope.infoMethod(); }
|
|
||||||
alert('Events updated !!!');
|
|
||||||
scope.events.duplicates = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.saveConflicts = function() {
|
|
||||||
alert('todo');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
orgsModule.directive('orgDetails', function($http, $location, $route) {
|
orgsModule.directive('orgDetails', function($http, $location, $route) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
|
@ -247,6 +198,7 @@ orgsModule.directive('orgResultsPage', function($http, $location, $route) {
|
||||||
'orgs' : '=',
|
'orgs' : '=',
|
||||||
'nextFunction' : '&',
|
'nextFunction' : '&',
|
||||||
'prevFunction' : '&',
|
'prevFunction' : '&',
|
||||||
|
'onSelect' : '&',
|
||||||
'selectedOrg' : '=',
|
'selectedOrg' : '=',
|
||||||
'mode' : '@'
|
'mode' : '@'
|
||||||
},
|
},
|
||||||
|
@ -255,94 +207,90 @@ orgsModule.directive('orgResultsPage', function($http, $location, $route) {
|
||||||
scope.selectOrg = function(o) {
|
scope.selectOrg = function(o) {
|
||||||
scope.selectedOrg.id = o.id;
|
scope.selectedOrg.id = o.id;
|
||||||
scope.selectedOrg.name = o.name;
|
scope.selectedOrg.name = o.name;
|
||||||
scope.selectedOrg.type = o.name;
|
scope.selectedOrg.type = o.type;
|
||||||
scope.selectedOrg.city = o.name;
|
scope.selectedOrg.city = o.city;
|
||||||
scope.selectedOrg.country = o.name;
|
scope.selectedOrg.country = o.country;
|
||||||
scope.selectedOrg.acronyms = o.name;
|
scope.selectedOrg.acronyms = o.acronyms;
|
||||||
scope.selectedOrg.approved = o.name;
|
scope.selectedOrg.urls = o.urls;
|
||||||
|
scope.selectedOrg.status = o.status;
|
||||||
|
|
||||||
|
if (scope.onSelect) {
|
||||||
|
scope.onSelect();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.directive('allConflicts', function($http, $location, $route, $q) {
|
orgsModule.directive('orgDuplicates', function($http, $location, $route) {
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
'conflicts' : '=',
|
|
||||||
'country' : '@',
|
|
||||||
'info' : '=',
|
|
||||||
'infoMethod' : '&'
|
|
||||||
},
|
|
||||||
templateUrl: 'resources/html/forms/all_conflicts.html',
|
|
||||||
link: function(scope, element, attrs, ctrl) {
|
|
||||||
scope.orgs = [];
|
|
||||||
|
|
||||||
scope.prepareConflictsModal = function(list) {
|
|
||||||
scope.orgs = [];
|
|
||||||
scope.selectedOrgs = [];
|
|
||||||
|
|
||||||
var gets = list.map((o) => $http.get('api/organizations/get?id=' + o.id));
|
|
||||||
|
|
||||||
$q.all(gets).then(function(responses) {
|
|
||||||
scope.orgs = responses.map((resp) => resp.data);
|
|
||||||
angular.forEach(scope.orgs, function(org) { org.show = 'secondary'; });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
orgsModule.directive('pendingOrgs', function($http, $location, $route, $q) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
'orgs' : '=',
|
|
||||||
'country' : '@',
|
|
||||||
'info' : '=',
|
|
||||||
'infoMethod' : '&'
|
|
||||||
},
|
|
||||||
templateUrl: 'resources/html/forms/pending_orgs.html',
|
|
||||||
link: function(scope, element, attrs, ctrl) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
orgsModule.directive('orgFormDuplicates', function($http, $location, $route) {
|
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
|
'orgId' : '@',
|
||||||
'duplicates' : '=',
|
'duplicates' : '=',
|
||||||
'showSaveButton' : '@',
|
'showSaveButton' : '@',
|
||||||
'saveFunction' : '&'
|
'saveFunction' : '&'
|
||||||
},
|
},
|
||||||
templateUrl: 'resources/html/forms/org_duplicates.html',
|
templateUrl: 'resources/html/parts/org_duplicates.html',
|
||||||
link: function(scope, element, attrs, ctrl) {}
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
scope.newDuplicate = {};
|
||||||
|
|
||||||
|
scope.addDuplicate = function() {
|
||||||
|
scope.duplicates.push({
|
||||||
|
'localId' : scope.orgId,
|
||||||
|
'oaOriginalId' : scope.newDuplicate.id,
|
||||||
|
'oaName' : scope.newDuplicate.name,
|
||||||
|
'oaAcronym' : scope.newDuplicate.acronyms.join(),
|
||||||
|
'oaCountry' : scope.newDuplicate.country,
|
||||||
|
'oaUrl' : scope.newDuplicate.urls.join(),
|
||||||
|
'oaCollectedFrom' : 'user',
|
||||||
|
'relType' : 'is_similar'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
call_http_get($http, 'api/organizations/duplicates?id=' + scope.newDuplicate.id, function(res) {
|
||||||
|
angular.forEach(res.data, function(dup) {
|
||||||
|
dup.localId = scope.orgId;
|
||||||
|
scope.duplicates.push(dup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.directive('orgFormConflicts', function($http, $location, $route, $q) {
|
orgsModule.directive('orgConflicts', function($http, $location, $route, $q) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
'orgId' : '@',
|
'org' : '=',
|
||||||
'conflicts' : '=',
|
'conflicts' : '=',
|
||||||
'showSaveButton' : '@',
|
'showSaveButton' : '@'
|
||||||
'saveFunction' : '&'
|
|
||||||
},
|
},
|
||||||
templateUrl: 'resources/html/forms/org_conflicts.html',
|
templateUrl: 'resources/html/parts/org_conflicts.html',
|
||||||
link: function(scope, element, attrs, ctrl) {
|
link: function(scope, element, attrs, ctrl) {
|
||||||
scope.candidateConflicts = [];
|
scope.candidateConflicts = [];
|
||||||
scope.selectedConflicts = [];
|
scope.selectedConflicts = [];
|
||||||
|
scope.newConflict = {};
|
||||||
|
|
||||||
|
scope.addConflict = function() {
|
||||||
|
scope.conflicts.push({
|
||||||
|
'id' : scope.newConflict.id,
|
||||||
|
'name' : scope.newConflict.name,
|
||||||
|
'type' : scope.newConflict.type,
|
||||||
|
'city' : scope.newConflict.city,
|
||||||
|
'country': scope.newConflict.country
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
scope.prepareConflictsModal = function() {
|
scope.prepareConflictsModal = function() {
|
||||||
scope.candidateConflicts = [];
|
scope.candidateConflicts = [];
|
||||||
scope.selectedConflicts = [];
|
scope.selectedConflicts = [];
|
||||||
|
|
||||||
var gets = [ $http.get('api/organizations/get?id=' + scope.orgId) ];
|
var gets = [ $http.get('api/organizations/get?id=' + scope.org.id) ];
|
||||||
angular.forEach(scope.conflicts, function(c) { gets.push($http.get('api/organizations/get?id=' + c.id)); });
|
angular.forEach(scope.conflicts, function(c) { gets.push($http.get('api/organizations/get?id=' + c.id)); });
|
||||||
|
|
||||||
$q.all(gets).then(function(responses) {
|
$q.all(gets).then(function(responses) {
|
||||||
|
@ -354,83 +302,21 @@ orgsModule.directive('orgFormConflicts', function($http, $location, $route, $q)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.directive('allDuplicates', function($http, $location, $route, $timeout) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
'duplicates' : '=',
|
|
||||||
'country' : '@',
|
|
||||||
'info' : '=',
|
|
||||||
'infoMethod' : '&'
|
|
||||||
},
|
|
||||||
templateUrl: 'resources/html/forms/all_duplicates.html',
|
|
||||||
link: function(scope, element, attrs, ctrl) {
|
|
||||||
scope.currentOrg = {};
|
|
||||||
scope.currentOrgDetails = {};
|
|
||||||
scope.currentDuplicates = [];
|
|
||||||
|
|
||||||
scope.prepareDuplicatesModal = function(org) {
|
|
||||||
scope.currentOrg = org;
|
|
||||||
scope.currentOrgDetails = {};
|
|
||||||
scope.currentDuplicates = [];
|
|
||||||
|
|
||||||
$http.get('api/organizations/get?id=' + org.id).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.currentOrgDetails = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
$http.get('api/organizations/duplicates?id=' + org.id).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
scope.currentDuplicates = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.saveCurrentDuplicates = function() {
|
|
||||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
|
||||||
$http.post('api/organizations/duplicates', scope.currentDuplicates).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
if (scope.infoMethod) { scope.infoMethod(); }
|
|
||||||
scope.currentOrg.numberOfDuplicates = 0;
|
|
||||||
for (var i=0; i<res.data.length; i++) {
|
|
||||||
if (res.data[i].relType == 'suggested') {
|
|
||||||
scope.currentOrg.numberOfDuplicates++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scope.currentDuplicates = [];
|
|
||||||
|
|
||||||
$timeout(function() {
|
|
||||||
if (scope.duplicates.length > 1) {
|
|
||||||
$route.reload();
|
|
||||||
} else {
|
|
||||||
$location.url('/suggestions/_/1');
|
|
||||||
}
|
|
||||||
}, 600);
|
|
||||||
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
orgsModule.config(function($routeProvider) {
|
orgsModule.config(function($routeProvider) {
|
||||||
$routeProvider
|
$routeProvider
|
||||||
.when('/new', { templateUrl: 'resources/html/new.html', controller: 'newOrgCtrl' })
|
.when('/search', { templateUrl: 'resources/html/pages/search/search.html', controller: 'searchCtrl' })
|
||||||
.when('/search', { templateUrl: 'resources/html/search.html', controller: 'searchCtrl' })
|
.when('/searchResults/:page/:size/:text*', { templateUrl: 'resources/html/pages/search/searchResults.html', controller: 'searchResultsCtrl' })
|
||||||
.when('/searchResults/:page/:size/:text*', { templateUrl: 'resources/html/searchResults.html', controller: 'searchResultsCtrl' })
|
.when('/countries', { templateUrl: 'resources/html/pages/search/browse.html', controller: 'countriesCtrl' })
|
||||||
.when('/countries', { templateUrl: 'resources/html/browse.html', controller: 'countriesCtrl' })
|
.when('/byCountry/:page/:size/:status/:code*', { templateUrl: 'resources/html/pages/search/resultsByCountry.html', controller: 'byCountryCtrl' })
|
||||||
.when('/byCountry/:page/:size/:code*', { templateUrl: 'resources/html/resultsByCountry.html', controller: 'byCountryCtrl' })
|
.when('/types', { templateUrl: 'resources/html/pages/search/browse.html', controller: 'typesCtrl' })
|
||||||
.when('/types', { templateUrl: 'resources/html/browse.html', controller: 'typesCtrl' })
|
.when('/byType/:page/:size/:status/:type*', { templateUrl: 'resources/html/pages/search/resultsByType.html', controller: 'byTypeCtrl' })
|
||||||
.when('/byType/:page/:size/:type*', { templateUrl: 'resources/html/resultsByType.html', controller: 'byTypeCtrl' })
|
.when('/edit/:msg/:id', { templateUrl: 'resources/html/pages/edit/edit.html', controller: 'showEditCtrl' })
|
||||||
.when('/edit/:msg/:id', { templateUrl: 'resources/html/edit.html', controller: 'showEditCtrl' })
|
.when('/new', { templateUrl: 'resources/html/pages/advanced/new.html', controller: 'newOrgCtrl' })
|
||||||
.when('/suggestions/:country/:tab', { templateUrl: 'resources/html/suggestions.html', controller: 'showSuggestionsCtrl' })
|
.when('/pendings/:country', { templateUrl: 'resources/html/pages/advanced/pendingOrgs.html', controller: 'pendingOrgsCtrl' })
|
||||||
.when('/users', { templateUrl: 'resources/html/users.html', controller: 'usersCtrl' })
|
.when('/duplicates/:country', { templateUrl: 'resources/html/pages/advanced/duplicates.html', controller: 'duplicatesCtrl' })
|
||||||
.otherwise({ redirectTo: '/suggestions/_/0' });
|
.when('/conflicts/:country', { templateUrl: 'resources/html/pages/advanced/conflicts.html', controller: 'conflictsCtrl' })
|
||||||
|
.when('/users', { templateUrl: 'resources/html/pages/admin/users.html', controller: 'usersCtrl' })
|
||||||
|
.otherwise({ redirectTo: '/search' });
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.filter('escape', function() {
|
orgsModule.filter('escape', function() {
|
||||||
|
@ -462,10 +348,9 @@ orgsModule.controller('newOrgCtrl', function ($scope, $http, $routeParams, $loca
|
||||||
"urls": [],
|
"urls": [],
|
||||||
"relations": []
|
"relations": []
|
||||||
};
|
};
|
||||||
|
$scope.adminMode = adminMode();
|
||||||
$scope.vocabularies = {};
|
$scope.vocabularies = {};
|
||||||
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
|
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.controller('searchCtrl', function ($scope, $location) {
|
orgsModule.controller('searchCtrl', function ($scope, $location) {
|
||||||
|
@ -486,12 +371,7 @@ orgsModule.controller('searchResultsCtrl', function ($scope, $http, $routeParams
|
||||||
}
|
}
|
||||||
$scope.orgs = {};
|
$scope.orgs = {};
|
||||||
|
|
||||||
$http.get('api/organizations/search/' + $routeParams.page + '/' + $routeParams.size + '?q=' + $scope.searchText).then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/search/' + $routeParams.page + '/' + $routeParams.size + '?q=' + $scope.searchText, function(res) { $scope.orgs = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.orgs = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.prev = function() {
|
$scope.prev = function() {
|
||||||
if ($scope.searchText) {
|
if ($scope.searchText) {
|
||||||
|
@ -511,16 +391,13 @@ orgsModule.controller('searchResultsCtrl', function ($scope, $http, $routeParams
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.controller('countriesCtrl', function ($scope, $http, $routeParams) {
|
orgsModule.controller('countriesCtrl', function ($scope, $http, $routeParams) {
|
||||||
|
|
||||||
|
$scope.title = 'Countries';
|
||||||
$scope.field = 'Country';
|
$scope.field = 'Country';
|
||||||
$scope.resultsBasePath = '/byCountry'
|
$scope.resultsBasePath = '/byCountry'
|
||||||
$scope.entries = [];
|
$scope.entries = [];
|
||||||
|
|
||||||
$http.get('api/organizations/browse/countries').then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/browse/countries', function(res) { $scope.entries = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.entries = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -528,35 +405,25 @@ orgsModule.controller('byCountryCtrl', function ($scope, $http, $routeParams, $l
|
||||||
$scope.fieldValue = decodeURIComponent($routeParams.code);
|
$scope.fieldValue = decodeURIComponent($routeParams.code);
|
||||||
$scope.orgs = {};
|
$scope.orgs = {};
|
||||||
|
|
||||||
$http.get('api/organizations/byCountry/' + $routeParams.code + '/' + $routeParams.page + '/' + $routeParams.size).then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/byCountry/' + $routeParams.status + '/' + $routeParams.code + '/' + $routeParams.page + '/' + $routeParams.size, function(res) { $scope.orgs = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.orgs = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.prev = function() {
|
$scope.prev = function() {
|
||||||
$location.url('/byCountry/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue));
|
$location.url('/byCountry/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.next = function() {
|
$scope.next = function() {
|
||||||
$location.url('/byCountry/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue));
|
$location.url('/byCountry/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.controller('typesCtrl', function ($scope, $http, $routeParams) {
|
orgsModule.controller('typesCtrl', function ($scope, $http, $routeParams) {
|
||||||
|
$scope.title = 'Organization types';
|
||||||
$scope.field = 'Organization type';
|
$scope.field = 'Organization type';
|
||||||
$scope.resultsBasePath = '/byType'
|
$scope.resultsBasePath = '/byType'
|
||||||
$scope.entries = [];
|
$scope.entries = [];
|
||||||
|
|
||||||
$http.get('api/organizations/browse/types').then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/browse/types', function(res) { $scope.entries = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.entries = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $location) {
|
orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $location) {
|
||||||
|
@ -565,19 +432,14 @@ orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $loca
|
||||||
|
|
||||||
$scope.orgs = {};
|
$scope.orgs = {};
|
||||||
|
|
||||||
$http.get('api/organizations/byType/' + $routeParams.type + '/' + $routeParams.page + '/' + $routeParams.size).then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/byType/' + $routeParams.status + '/' + $routeParams.type + '/' + $routeParams.page + '/' + $routeParams.size, function(res) { $scope.orgs = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.orgs = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.prev = function() {
|
$scope.prev = function() {
|
||||||
$location.url('/byType/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue));
|
$location.url('/byType/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.next = function() {
|
$scope.next = function() {
|
||||||
$location.url('/byType/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue));
|
$location.url('/byType/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -585,89 +447,81 @@ orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $loca
|
||||||
orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $route, $location, $timeout, $window, vocabulariesService) {
|
orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $route, $location, $timeout, $window, vocabulariesService) {
|
||||||
$scope.orgId = $routeParams.id;
|
$scope.orgId = $routeParams.id;
|
||||||
$scope.org = {};
|
$scope.org = {};
|
||||||
$scope.events = {};
|
$scope.duplicates = [];
|
||||||
|
$scope.conflicts = [];
|
||||||
$scope.info = {};
|
$scope.info = {};
|
||||||
|
$scope.currentTab = 1
|
||||||
|
$scope.vocabularies = {};
|
||||||
|
$scope.adminMode = adminMode();
|
||||||
|
|
||||||
$scope.getInfo = function() {
|
$scope.getInfo = function() {
|
||||||
$http.get('api/organizations/info?id=' + $scope.orgId).then(function successCallback(res) {
|
call_http_get($http, 'api/organizations/info?id=' + $scope.orgId, function(res) { $scope.info = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.info = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getInfo();
|
$scope.gotoTab = function(tab) {
|
||||||
|
$scope.org = {};
|
||||||
|
call_http_get($http, 'api/organizations/get?id=' + $scope.orgId, function(res) { $scope.org = res.data; });
|
||||||
|
|
||||||
|
if (tab == 2) {
|
||||||
|
$scope.duplicates = [];
|
||||||
|
call_http_get($http, 'api/organizations/duplicates?id=' + $scope.orgId, function(res) { $scope.duplicates = res.data; });
|
||||||
|
} else if (tab == 3) {
|
||||||
|
$scope.conflicts = [];
|
||||||
|
call_http_get($http, 'api/organizations/conflicts?id=' + $scope.orgId, function(res) { $scope.conflicts = res.data; });
|
||||||
|
}
|
||||||
|
$scope.currentTab = tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$scope.saveDuplicates = function() {
|
||||||
|
|
||||||
|
call_http_post($http, 'api/organizations/duplicates', $scope.duplicates, function(res) {
|
||||||
|
$scope.getInfo();
|
||||||
|
alert('Events updated !!!');
|
||||||
|
$scope.duplicates = res.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.vocabularies = {};
|
|
||||||
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
|
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
|
||||||
|
$scope.getInfo();
|
||||||
|
$scope.gotoTab(1);
|
||||||
|
|
||||||
if ($routeParams.msg == 1) { $scope.message = 'New organization registered !!!'; }
|
if ($routeParams.msg == 1) { $scope.message = 'New organization registered !!!'; }
|
||||||
else if ($routeParams.msg == 2) { $scope.message = 'Organization updated !!!'; }
|
else if ($routeParams.msg == 2) { $scope.message = 'Organization updated !!!'; }
|
||||||
else if ($routeParams.msg == 3) { $scope.message = 'Pending organization registered !!!'; }
|
else if ($routeParams.msg == 3) { $scope.message = 'Pending organization registered !!!'; }
|
||||||
else { $scope.message = ''; }
|
else { $scope.message = ''; }
|
||||||
$window.scrollTo(0, 0);
|
$window.scrollTo(0, 0);
|
||||||
|
|
||||||
$timeout(function() { $scope.message = ''; }, 3000)
|
$timeout(function() { $scope.message = ''; }, 3000);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routeParams, $location) {
|
orgsModule.controller('pendingOrgsCtrl', function ($scope, $http, $routeParams, $location, suggestionInfo) {
|
||||||
$scope.info = {};
|
$scope.info = suggestionInfo.getInfo();
|
||||||
$scope.pendingOrgs = [];
|
$scope.orgs = [];
|
||||||
$scope.duplicates = [];
|
|
||||||
$scope.conflicts = [];
|
|
||||||
$scope.currentTab = $routeParams.tab;
|
|
||||||
$scope.country = $routeParams.country;
|
$scope.country = $routeParams.country;
|
||||||
|
|
||||||
$scope.getInfo = function() {
|
$scope.getInfo = function() {
|
||||||
$http.get('api/organizations/suggestionsInfo').then(function successCallback(res) {
|
suggestionInfo.updateInfo(function(info) {
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
if ($scope.country == '_') {
|
||||||
$scope.info = res.data;
|
var found = '';
|
||||||
if ($scope.country == '_') {
|
angular.forEach(info.data.byCountry, function(values, c) {
|
||||||
var found = '';
|
if (!found && values.nPendingOrgs > 0) {
|
||||||
|
found = c;
|
||||||
angular.forEach($scope.info.byCountry, function(values, c) {
|
}
|
||||||
if (!found && (($scope.currentTab == 0 && values.nPendingOrgs > 0) || ($scope.currentTab == 1 && values.nDuplicates > 0) || ($scope.currentTab == 2 && values.nConflicts > 0))) {
|
});
|
||||||
found = c;
|
if (found) { $location.url('/pendings/' + found); }
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (found) { $location.url('/suggestions/' + found + '/' + $scope.currentTab); }
|
|
||||||
}
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$scope.refresh = function() {
|
$scope.refresh = function() {
|
||||||
$scope.pendingOrgs = [];
|
$scope.orgs = [];
|
||||||
$scope.duplicates = [];
|
|
||||||
$scope.conflicts = [];
|
|
||||||
|
|
||||||
if ($scope.country != '_') {
|
if ($scope.country != '_') {
|
||||||
if ($scope.currentTab == 0) {
|
call_http_get($http, 'api/organizations/byCountry/suggested/' + $scope.country, function(res) { $scope.orgs = res.data; });
|
||||||
$http.get('api/organizations/byCountry/pending/' + $scope.country).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.pendingOrgs = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
} else if ($scope.currentTab == 1) {
|
|
||||||
$http.get('api/organizations/duplicates/byCountry/' + $scope.country).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.duplicates = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
} else if ($scope.currentTab == 2) {
|
|
||||||
$http.get('api/organizations/conflicts/byCountry/' + $scope.country).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.conflicts = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
} else { }
|
|
||||||
}
|
}
|
||||||
$scope.getInfo();
|
$scope.getInfo();
|
||||||
}
|
}
|
||||||
|
@ -675,6 +529,135 @@ orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routePara
|
||||||
$scope.refresh();
|
$scope.refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
orgsModule.controller('duplicatesCtrl', function ($scope, $http, $routeParams, $location, $timeout, $route, suggestionInfo) {
|
||||||
|
$scope.info = suggestionInfo.getInfo();
|
||||||
|
$scope.duplicates = [];
|
||||||
|
$scope.country = $routeParams.country;
|
||||||
|
$scope.currentOrg = {};
|
||||||
|
$scope.currentOrgDetails = {};
|
||||||
|
|
||||||
|
$scope.prepareDuplicatesModal = function(org) {
|
||||||
|
$scope.currentOrg = org;
|
||||||
|
$scope.currentOrgDetails = {};
|
||||||
|
$scope.currentDuplicates = [];
|
||||||
|
|
||||||
|
call_http_get($http, 'api/organizations/get?id=' + org.id, function(res) { $scope.currentOrgDetails = res.data; });
|
||||||
|
call_http_get($http, 'api/organizations/duplicates?id=' + org.id, function(res) { $scope.currentDuplicates = res.data; });
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getInfo = function() {
|
||||||
|
suggestionInfo.updateInfo(function(info) {
|
||||||
|
if ($scope.country == '_') {
|
||||||
|
var found = '';
|
||||||
|
angular.forEach(info.data.byCountry, function(values, c) {
|
||||||
|
if (!found && values.nDuplicates > 0) {
|
||||||
|
found = c;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (found) { $location.url('/duplicates/' + found); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.saveCurrentDuplicates = function() {
|
||||||
|
|
||||||
|
call_http_post($http, 'api/organizations/duplicates', $scope.currentDuplicates, function(res) {
|
||||||
|
|
||||||
|
$scope.getInfo();
|
||||||
|
|
||||||
|
$scope.currentOrg.numberOfDuplicates = 0;
|
||||||
|
for (var i=0; i<res.data.length; i++) {
|
||||||
|
if (res.data[i].relType == 'suggested') {
|
||||||
|
$scope.currentOrg.numberOfDuplicates++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.currentDuplicates = [];
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
if ($scope.duplicates.length > 1) {
|
||||||
|
$route.reload();
|
||||||
|
} else {
|
||||||
|
$location.url('/duplicates/_');
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.refresh = function() {
|
||||||
|
$scope.duplicates = [];
|
||||||
|
|
||||||
|
if ($scope.country != '_') {
|
||||||
|
call_http_get($http, 'api/organizations/duplicates/byCountry/' + $scope.country, function(res) { $scope.duplicates = res.data; });
|
||||||
|
}
|
||||||
|
$scope.getInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
orgsModule.controller('conflictsCtrl', function ($scope, $http, $routeParams, $location, $route, $q, suggestionInfo) {
|
||||||
|
$scope.info = suggestionInfo.getInfo();
|
||||||
|
$scope.conflicts = [];
|
||||||
|
$scope.country = $routeParams.country;
|
||||||
|
$scope.orgs = [];
|
||||||
|
$scope.newConflict = {};
|
||||||
|
$scope.currentGroup = [];
|
||||||
|
|
||||||
|
$scope.addConflict = function() {
|
||||||
|
$scope.currentGroup.push({
|
||||||
|
'id' : $scope.newConflict.id,
|
||||||
|
'name' : $scope.newConflict.name,
|
||||||
|
'type' : $scope.newConflict.type,
|
||||||
|
'city' : $scope.newConflict.city,
|
||||||
|
'country': $scope.newConflict.country
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.prepareAddConflictModal = function(list) {
|
||||||
|
$scope.currentGroup = list;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.prepareConflictsModal = function(list) {
|
||||||
|
$scope.orgs = [];
|
||||||
|
$scope.selectedOrgs = [];
|
||||||
|
|
||||||
|
var gets = list.map((o) => $http.get('api/organizations/get?id=' + o.id));
|
||||||
|
|
||||||
|
$q.all(gets).then(function(responses) {
|
||||||
|
$scope.orgs = responses.map((resp) => resp.data);
|
||||||
|
angular.forEach($scope.orgs, function(org) { org.show = 'secondary'; });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.getInfo = function() {
|
||||||
|
suggestionInfo.updateInfo(function(info) {
|
||||||
|
if ($scope.country == '_') {
|
||||||
|
var found = '';
|
||||||
|
|
||||||
|
angular.forEach(info.data.byCountry, function(values, c) {
|
||||||
|
if (!found && values.nConflicts > 0) {
|
||||||
|
found = c;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (found) { $location.url('/conflicts/' + found); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.refresh = function() {
|
||||||
|
$scope.conflicts = [];
|
||||||
|
|
||||||
|
if ($scope.country != '_') {
|
||||||
|
call_http_get($http, 'api/organizations/conflicts/byCountry/' + $scope.country, function(res) { $scope.conflicts = res.data; });
|
||||||
|
}
|
||||||
|
$scope.getInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vocabulariesService) {
|
orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vocabulariesService) {
|
||||||
$scope.users = [];
|
$scope.users = [];
|
||||||
|
@ -685,12 +668,7 @@ orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vo
|
||||||
$scope.vocabularies = {};
|
$scope.vocabularies = {};
|
||||||
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
|
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
|
||||||
|
|
||||||
$http.get('api/users').then(function successCallback(res) {
|
call_http_get($http, 'api/users', function(res) { $scope.users = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.users = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.setCurrentUser = function(user) {
|
$scope.setCurrentUser = function(user) {
|
||||||
angular.copy(user, $scope.currentUser);
|
angular.copy(user, $scope.currentUser);
|
||||||
|
@ -700,23 +678,12 @@ orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vo
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.saveUser = function(user) {
|
$scope.saveUser = function(user) {
|
||||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
call_http_post($http, 'api/users', user, function(res) { $scope.users = res.data; });
|
||||||
$http.post('api/users', user).then(function successCallback(res) {
|
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.users = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.deleteUser = function(email) {
|
$scope.deleteUser = function(email) {
|
||||||
if (confirm("Are you sure ?")) {
|
if (confirm("Are you sure ?")) {
|
||||||
$http.delete('api/users?email=' + email).then(function successCallback(res) {
|
call_http_delete($http, 'api/users?email=' + email, function(res) { $scope.users = res.data; });
|
||||||
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
|
|
||||||
$scope.users = res.data;
|
|
||||||
}, function errorCallback(res) {
|
|
||||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,348 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2014 Felix Gnass
|
||||||
|
* Licensed under the MIT license
|
||||||
|
*/
|
||||||
|
(function(root, factory) {
|
||||||
|
|
||||||
|
/* CommonJS */
|
||||||
|
if (typeof exports == 'object') module.exports = factory()
|
||||||
|
|
||||||
|
/* AMD module */
|
||||||
|
else if (typeof define == 'function' && define.amd) define(factory)
|
||||||
|
|
||||||
|
/* Browser global */
|
||||||
|
else root.Spinner = factory()
|
||||||
|
}
|
||||||
|
(this, function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
|
||||||
|
, animations = {} /* Animation rules keyed by their name */
|
||||||
|
, useCssAnimations /* Whether to use CSS animations or setTimeout */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to create elements. If no tag name is given,
|
||||||
|
* a DIV is created. Optionally properties can be passed.
|
||||||
|
*/
|
||||||
|
function createEl(tag, prop) {
|
||||||
|
var el = document.createElement(tag || 'div')
|
||||||
|
, n
|
||||||
|
|
||||||
|
for(n in prop) el[n] = prop[n]
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends children and returns the parent.
|
||||||
|
*/
|
||||||
|
function ins(parent /* child1, child2, ...*/) {
|
||||||
|
for (var i=1, n=arguments.length; i<n; i++)
|
||||||
|
parent.appendChild(arguments[i])
|
||||||
|
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a new stylesheet to hold the @keyframe or VML rules.
|
||||||
|
*/
|
||||||
|
var sheet = (function() {
|
||||||
|
var el = createEl('style', {type : 'text/css'})
|
||||||
|
ins(document.getElementsByTagName('head')[0], el)
|
||||||
|
return el.sheet || el.styleSheet
|
||||||
|
}())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an opacity keyframe animation rule and returns its name.
|
||||||
|
* Since most mobile Webkits have timing issues with animation-delay,
|
||||||
|
* we create separate rules for each line/segment.
|
||||||
|
*/
|
||||||
|
function addAnimation(alpha, trail, i, lines) {
|
||||||
|
var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
|
||||||
|
, start = 0.01 + i/lines * 100
|
||||||
|
, z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
|
||||||
|
, prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
|
||||||
|
, pre = prefix && '-' + prefix + '-' || ''
|
||||||
|
|
||||||
|
if (!animations[name]) {
|
||||||
|
sheet.insertRule(
|
||||||
|
'@' + pre + 'keyframes ' + name + '{' +
|
||||||
|
'0%{opacity:' + z + '}' +
|
||||||
|
start + '%{opacity:' + alpha + '}' +
|
||||||
|
(start+0.01) + '%{opacity:1}' +
|
||||||
|
(start+trail) % 100 + '%{opacity:' + alpha + '}' +
|
||||||
|
'100%{opacity:' + z + '}' +
|
||||||
|
'}', sheet.cssRules.length)
|
||||||
|
|
||||||
|
animations[name] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries various vendor prefixes and returns the first supported property.
|
||||||
|
*/
|
||||||
|
function vendor(el, prop) {
|
||||||
|
var s = el.style
|
||||||
|
, pp
|
||||||
|
, i
|
||||||
|
|
||||||
|
prop = prop.charAt(0).toUpperCase() + prop.slice(1)
|
||||||
|
for(i=0; i<prefixes.length; i++) {
|
||||||
|
pp = prefixes[i]+prop
|
||||||
|
if(s[pp] !== undefined) return pp
|
||||||
|
}
|
||||||
|
if(s[prop] !== undefined) return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets multiple style properties at once.
|
||||||
|
*/
|
||||||
|
function css(el, prop) {
|
||||||
|
for (var n in prop)
|
||||||
|
el.style[vendor(el, n)||n] = prop[n]
|
||||||
|
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills in default values.
|
||||||
|
*/
|
||||||
|
function merge(obj) {
|
||||||
|
for (var i=1; i < arguments.length; i++) {
|
||||||
|
var def = arguments[i]
|
||||||
|
for (var n in def)
|
||||||
|
if (obj[n] === undefined) obj[n] = def[n]
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute page-offset of the given element.
|
||||||
|
*/
|
||||||
|
function pos(el) {
|
||||||
|
var o = { x:el.offsetLeft, y:el.offsetTop }
|
||||||
|
while((el = el.offsetParent))
|
||||||
|
o.x+=el.offsetLeft, o.y+=el.offsetTop
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the line color from the given string or array.
|
||||||
|
*/
|
||||||
|
function getColor(color, idx) {
|
||||||
|
return typeof color == 'string' ? color : color[idx % color.length]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Built-in defaults
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
lines: 12, // The number of lines to draw
|
||||||
|
length: 7, // The length of each line
|
||||||
|
width: 5, // The line thickness
|
||||||
|
radius: 10, // The radius of the inner circle
|
||||||
|
rotate: 0, // Rotation offset
|
||||||
|
corners: 1, // Roundness (0..1)
|
||||||
|
color: '#000', // #rgb or #rrggbb
|
||||||
|
direction: 1, // 1: clockwise, -1: counterclockwise
|
||||||
|
speed: 1, // Rounds per second
|
||||||
|
trail: 100, // Afterglow percentage
|
||||||
|
opacity: 1/4, // Opacity of the lines
|
||||||
|
fps: 20, // Frames per second when using setTimeout()
|
||||||
|
zIndex: 2e9, // Use a high z-index by default
|
||||||
|
className: 'spinner', // CSS class to assign to the element
|
||||||
|
top: '50%', // center vertically
|
||||||
|
left: '50%', // center horizontally
|
||||||
|
position: 'absolute' // element position
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The constructor */
|
||||||
|
function Spinner(o) {
|
||||||
|
this.opts = merge(o || {}, Spinner.defaults, defaults)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global defaults that override the built-ins:
|
||||||
|
Spinner.defaults = {}
|
||||||
|
|
||||||
|
merge(Spinner.prototype, {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the spinner to the given target element. If this instance is already
|
||||||
|
* spinning, it is automatically removed from its previous target b calling
|
||||||
|
* stop() internally.
|
||||||
|
*/
|
||||||
|
spin: function(target) {
|
||||||
|
this.stop()
|
||||||
|
|
||||||
|
var self = this
|
||||||
|
, o = self.opts
|
||||||
|
, el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
|
||||||
|
, mid = o.radius+o.length+o.width
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
target.insertBefore(el, target.firstChild||null)
|
||||||
|
css(el, {
|
||||||
|
left: o.left,
|
||||||
|
top: o.top
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
el.setAttribute('role', 'progressbar')
|
||||||
|
self.lines(el, self.opts)
|
||||||
|
|
||||||
|
if (!useCssAnimations) {
|
||||||
|
// No CSS animation support, use setTimeout() instead
|
||||||
|
var i = 0
|
||||||
|
, start = (o.lines - 1) * (1 - o.direction) / 2
|
||||||
|
, alpha
|
||||||
|
, fps = o.fps
|
||||||
|
, f = fps/o.speed
|
||||||
|
, ostep = (1-o.opacity) / (f*o.trail / 100)
|
||||||
|
, astep = f/o.lines
|
||||||
|
|
||||||
|
;(function anim() {
|
||||||
|
i++;
|
||||||
|
for (var j = 0; j < o.lines; j++) {
|
||||||
|
alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
|
||||||
|
|
||||||
|
self.opacity(el, j * o.direction + start, alpha, o)
|
||||||
|
}
|
||||||
|
self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops and removes the Spinner.
|
||||||
|
*/
|
||||||
|
stop: function() {
|
||||||
|
var el = this.el
|
||||||
|
if (el) {
|
||||||
|
clearTimeout(this.timeout)
|
||||||
|
if (el.parentNode) el.parentNode.removeChild(el)
|
||||||
|
this.el = undefined
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method that draws the individual lines. Will be overwritten
|
||||||
|
* in VML fallback mode below.
|
||||||
|
*/
|
||||||
|
lines: function(el, o) {
|
||||||
|
var i = 0
|
||||||
|
, start = (o.lines - 1) * (1 - o.direction) / 2
|
||||||
|
, seg
|
||||||
|
|
||||||
|
function fill(color, shadow) {
|
||||||
|
return css(createEl(), {
|
||||||
|
position: 'absolute',
|
||||||
|
width: (o.length+o.width) + 'px',
|
||||||
|
height: o.width + 'px',
|
||||||
|
background: color,
|
||||||
|
boxShadow: shadow,
|
||||||
|
transformOrigin: 'left',
|
||||||
|
transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
|
||||||
|
borderRadius: (o.corners * o.width>>1) + 'px'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < o.lines; i++) {
|
||||||
|
seg = css(createEl(), {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 1+~(o.width/2) + 'px',
|
||||||
|
transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
|
||||||
|
opacity: o.opacity,
|
||||||
|
animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
|
||||||
|
ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)')))
|
||||||
|
}
|
||||||
|
return el
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method that adjusts the opacity of a single line.
|
||||||
|
* Will be overwritten in VML fallback mode below.
|
||||||
|
*/
|
||||||
|
opacity: function(el, i, val) {
|
||||||
|
if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function initVML() {
|
||||||
|
|
||||||
|
/* Utility function to create a VML tag */
|
||||||
|
function vml(tag, attr) {
|
||||||
|
return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No CSS transforms but VML support, add a CSS rule for VML elements:
|
||||||
|
sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
|
||||||
|
|
||||||
|
Spinner.prototype.lines = function(el, o) {
|
||||||
|
var r = o.length+o.width
|
||||||
|
, s = 2*r
|
||||||
|
|
||||||
|
function grp() {
|
||||||
|
return css(
|
||||||
|
vml('group', {
|
||||||
|
coordsize: s + ' ' + s,
|
||||||
|
coordorigin: -r + ' ' + -r
|
||||||
|
}),
|
||||||
|
{ width: s, height: s }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var margin = -(o.width+o.length)*2 + 'px'
|
||||||
|
, g = css(grp(), {position: 'absolute', top: margin, left: margin})
|
||||||
|
, i
|
||||||
|
|
||||||
|
function seg(i, dx, filter) {
|
||||||
|
ins(g,
|
||||||
|
ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
|
||||||
|
ins(css(vml('roundrect', {arcsize: o.corners}), {
|
||||||
|
width: r,
|
||||||
|
height: o.width,
|
||||||
|
left: o.radius,
|
||||||
|
top: -o.width>>1,
|
||||||
|
filter: filter
|
||||||
|
}),
|
||||||
|
vml('fill', {color: getColor(o.color, i), opacity: o.opacity}),
|
||||||
|
vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.shadow)
|
||||||
|
for (i = 1; i <= o.lines; i++)
|
||||||
|
seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
|
||||||
|
|
||||||
|
for (i = 1; i <= o.lines; i++) seg(i)
|
||||||
|
return ins(el, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spinner.prototype.opacity = function(el, i, val, o) {
|
||||||
|
var c = el.firstChild
|
||||||
|
o = o.shadow && o.lines || 0
|
||||||
|
if (c && i+o < c.childNodes.length) {
|
||||||
|
c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild
|
||||||
|
if (c) c.opacity = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var probe = css(createEl('group'), {behavior: 'url(#default#VML)'})
|
||||||
|
|
||||||
|
if (!vendor(probe, 'transform') && probe.adj) initVML()
|
||||||
|
else useCssAnimations = vendor(probe, 'animation')
|
||||||
|
|
||||||
|
return Spinner
|
||||||
|
|
||||||
|
}));
|
|
@ -15,15 +15,27 @@
|
||||||
<link rel="stylesheet" href="resources/css/fontawesome-all.min.css">
|
<link rel="stylesheet" href="resources/css/fontawesome-all.min.css">
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.table > tbody > tr > td {
|
.table > tbody > tr > td { vertical-align : middle !important; }
|
||||||
vertical-align: middle;
|
.card > .table { margin-bottom : 0 !important; }
|
||||||
}
|
fieldset > legend { font-size : 1.2rem !important; }
|
||||||
.card > .table {
|
.overlaydiv {
|
||||||
margin-bottom: 0 !important;
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 10000;
|
||||||
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset > legend {
|
.grayRectangle {
|
||||||
font-size: 1.2rem !important;
|
position: absolute;
|
||||||
|
background-color: black;
|
||||||
|
opacity:0.6;
|
||||||
|
top: 30%;
|
||||||
|
left: 40%;
|
||||||
|
width: 20%;
|
||||||
|
height: 20%;
|
||||||
|
z-index: 100;
|
||||||
|
border-radius: 15px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -33,7 +45,12 @@ fieldset > legend {
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body ng-app="orgs" sec:authorize="isAuthenticated()">
|
<body ng-app="orgs" sec:authorize="isAuthenticated()">
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
|
||||||
|
<div id="spinnerdiv" class="overlaydiv">
|
||||||
|
<span class="grayRectangle"><!--The spinner is added on loading here--></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary" ng-controller="menuCtrl">
|
||||||
<a class="navbar-brand" href="#"> <img
|
<a class="navbar-brand" href="#"> <img
|
||||||
src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database">
|
src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database">
|
||||||
OpenOrgs Database
|
OpenOrgs Database
|
||||||
|
@ -45,7 +62,6 @@ fieldset > legend {
|
||||||
|
|
||||||
<div class="collapse navbar-collapse w-100 order-1" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse w-100 order-1" id="navbarSupportedContent">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
<li class="nav-item"><a class="nav-link" href="#!/suggestions/_/0">Last suggestions</a></li>
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Search</a>
|
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Search</a>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
|
@ -55,17 +71,25 @@ fieldset > legend {
|
||||||
<a class="dropdown-item" href="#!/types">browse by type</a>
|
<a class="dropdown-item" href="#!/types">browse by type</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_ADMIN') or hasRole('ROLE_NATIONAL_ADMIN')">
|
||||||
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Actions</a>
|
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Advanced</a>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<a class="dropdown-item" href="#!/new">register an organization</a>
|
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/pendings/_"><span class="pr-2">pending organizations</span><span class="badge badge-primary badge-pill">{{info.data.total.nPendingOrgs}}</span></a>
|
||||||
|
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/duplicates/_"><span class="pr-2">duplicates</span><span class="badge badge-primary badge-pill">{{info.data.total.nDuplicates}}</span></a>
|
||||||
|
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/conflicts/_"><span class="pr-2">conflicts</span><span class="badge badge-danger badge-pill">{{info.data.total.nConflicts}}</span></a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="#!/new">new from scratch</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_USER')">
|
||||||
|
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Advanced</a>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/duplicates/_"><span class="pr-2">duplicates</span><span class="badge badge-primary badge-pill">{{info.data.total.nDuplicates}}</span></a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="#!/new">suggest a new organization</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar-collapse collapse w-100 order-2">
|
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_ADMIN') or hasRole('ROLE_NATIONAL_ADMIN')">
|
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_ADMIN') or hasRole('ROLE_NATIONAL_ADMIN')">
|
||||||
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
|
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
|
||||||
|
@ -84,7 +108,7 @@ fieldset > legend {
|
||||||
<a class="dropdown-item" th:href="@{/logout}">Logout</a>
|
<a class="dropdown-item" th:href="@{/logout}">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item"><a class="btn btn-outline-secondary" href="doc">API</a></li>
|
<li class="nav-item"><a class="btn btn-outline-secondary" href="doc" target="_blank">API</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -98,6 +122,12 @@ fieldset > legend {
|
||||||
<script sec:authorize="!hasRole('ROLE_ADMIN')">
|
<script sec:authorize="!hasRole('ROLE_ADMIN')">
|
||||||
function superAdminMode() { return false; }
|
function superAdminMode() { return false; }
|
||||||
</script>
|
</script>
|
||||||
|
<script sec:authorize="hasRole('ROLE_ADMIN') or hasRole('ROLE_NATIONAL_ADMIN')">
|
||||||
|
function adminMode() { return true; }
|
||||||
|
</script>
|
||||||
|
<script sec:authorize="!hasRole('ROLE_ADMIN') and !hasRole('ROLE_NATIONAL_ADMIN')">
|
||||||
|
function adminMode() { return false; }
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="resources/js/jquery-3.4.1.min.js"></script>
|
<script src="resources/js/jquery-3.4.1.min.js"></script>
|
||||||
<script src="resources/js/popper.min.js"></script>
|
<script src="resources/js/popper.min.js"></script>
|
||||||
|
@ -106,6 +136,80 @@ fieldset > legend {
|
||||||
<script src="resources/js/angular-route.min.js"></script>
|
<script src="resources/js/angular-route.min.js"></script>
|
||||||
<script src='resources/js/checklist-model.js'></script>
|
<script src='resources/js/checklist-model.js'></script>
|
||||||
<script src="resources/js/organizations.js"></script>
|
<script src="resources/js/organizations.js"></script>
|
||||||
|
<script src="resources/js/spin.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Spinner show/hide methods ~ Andrea Mannocci
|
||||||
|
var spinnerOpts = {
|
||||||
|
lines: 15,
|
||||||
|
length: 16,
|
||||||
|
width: 5,
|
||||||
|
radius: 25,
|
||||||
|
color: '#eeeeee',
|
||||||
|
className: 'spinner',
|
||||||
|
top: '40%'
|
||||||
|
};
|
||||||
|
|
||||||
|
var spinnerTarget = document.getElementById('spinnerdiv');
|
||||||
|
|
||||||
|
var spinner;
|
||||||
|
|
||||||
|
function showSpinner() {
|
||||||
|
spinner = new Spinner(spinnerOpts).spin(spinnerTarget);
|
||||||
|
spinnerTarget.style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSpinner() {
|
||||||
|
spinnerTarget.style.visibility = 'hidden';
|
||||||
|
spinner.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function call_http_get($http, url, onSuccess) {
|
||||||
|
showSpinner();
|
||||||
|
$http.get(url).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 + ')');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function call_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 + ')');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function call_http_delete($http, url, onSuccess) {
|
||||||
|
showSpinner();
|
||||||
|
$http.delete(url).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>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue