/**
* Copyright © 2002 Instituto Superior Técnico
*
* This file is part of FenixEdu Academic.
*
* FenixEdu Academic is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu Academic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu Academic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fenixedu.academic.domain.accessControl.academicAdministration;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.fenixedu.academic.domain.AcademicProgram;
import org.fenixedu.academic.domain.Degree;
import org.fenixedu.academic.domain.accessControl.rules.AccessRule;
import org.fenixedu.academic.domain.accessControl.rules.AccessRuleSystem;
import org.fenixedu.academic.domain.accessControl.rules.AccessTarget;
import org.fenixedu.academic.domain.administrativeOffice.AdministrativeOffice;
import org.fenixedu.academic.domain.degree.DegreeType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.phd.PhdProgram;
import org.fenixedu.bennu.core.domain.User;
import org.fenixedu.bennu.core.groups.Group;
import org.joda.time.DateTime;
public class AcademicAccessRule extends AcademicAccessRule_Base implements Comparable<AcademicAccessRule> {
public static abstract class AcademicAccessTarget implements AccessTarget {
public abstract void write(AcademicAccessRule academicAccessRule, AcademicOperationType operation);
}
public static class AcademicProgramAccessTarget extends AcademicAccessTarget {
private final AcademicProgram program;
public AcademicProgramAccessTarget(AcademicProgram program) {
this.program = program;
}
public AcademicProgram getProgram() {
return program;
}
@Override
public void write(AcademicAccessRule academicAccessRule, AcademicOperationType operation) {
if (!operation.isProgramAllowedAsTarget()) {
throw new DomainException("error.persistent.authorization.group.does.not.allow.offices");
}
academicAccessRule.addProgram(program);
}
}
public static class AdministrativeOfficeAccessTarget extends AcademicAccessTarget {
private final AdministrativeOffice office;
public AdministrativeOfficeAccessTarget(AdministrativeOffice office) {
this.office = office;
}
public AdministrativeOffice getOffice() {
return office;
}
@Override
public void write(AcademicAccessRule academicAccessRule, AcademicOperationType operation) {
if (!operation.isOfficeAllowedAsTarget()) {
throw new DomainException("error.persistent.authorization.group.does.not.allow.offices");
}
academicAccessRule.addOffice(office);
}
}
public AcademicAccessRule(AcademicOperationType operation, Group whoCanAccess, Set<AcademicAccessTarget> whatCanAffect) {
super();
setOperation(operation);
setPersistentGroup(whoCanAccess.toPersistentGroup());
for (AcademicAccessTarget target : whatCanAffect) {
target.write(this, operation);
}
}
@Override
public AcademicOperationType getOperation() {
return (AcademicOperationType) super.getOperation();
}
@Override
public Set<AdministrativeOffice> getOfficeSet() {
// TODO remove when framework supports read-only slots
return super.getOfficeSet();
}
@Override
public Set<AcademicProgram> getProgramSet() {
// TODO remove when framework supports read-only slots
return super.getProgramSet();
}
@Override
public <T extends AccessTarget> Set<T> getWhatCanAffect() {
return (Set<T>) Stream.concat(getProgramSet().stream().map(AcademicProgramAccessTarget::new),
getOfficeSet().stream().map(AdministrativeOfficeAccessTarget::new)).collect(Collectors.toSet());
}
public AcademicAccessRule changeProgramsAndOffices(Set<AcademicProgram> programs, Set<AdministrativeOffice> offices) {
Set<AccessTarget> targets = new HashSet<>();
if (programs != null) {
programs.stream().forEach(p -> targets.add(new AcademicProgramAccessTarget(p)));
}
if (offices != null) {
offices.stream().forEach(p -> targets.add(new AdministrativeOfficeAccessTarget(p)));
}
return (AcademicAccessRule) changeWhatCanAffect(targets).get();
}
public Stream<AcademicProgram> getFullProgramSet() {
Stream<AcademicProgram> managed = getOfficeSet().stream().flatMap(o -> o.getManagedAcademicProgramSet().stream());
return Stream.concat(Stream.concat(getProgramSet().stream(), managed), Stream.of(Degree.readEmptyDegree()));
}
public static Stream<AcademicAccessRule> accessRules() {
return AccessRuleSystem.accessRules().filter(r -> r instanceof AcademicAccessRule).map(r -> (AcademicAccessRule) r);
}
public static Stream<AcademicAccessRule> accessRules(DateTime when) {
return AccessRuleSystem.accessRules(when).filter(r -> r instanceof AcademicAccessRule).map(r -> (AcademicAccessRule) r);
}
protected static Stream<AcademicAccessRule> filter(AcademicOperationType function) {
return accessRules().filter(r -> r.getOperation().equals(function));
}
protected static Stream<AcademicAccessRule> filter(AcademicOperationType function, DateTime when) {
return accessRules(when).filter(r -> r.getOperation().equals(function));
}
protected static Stream<AcademicAccessRule> filter(AcademicOperationType function, Set<AcademicProgram> programs,
Set<AdministrativeOffice> offices) {
Stream<AcademicAccessRule> stream = filter(function);
if (programs != null && !programs.isEmpty()) {
stream = stream.filter(r -> r.getFullProgramSet().collect(Collectors.toSet()).containsAll(programs));
}
if (offices != null && !offices.isEmpty()) {
stream = stream.filter(r -> r.getOfficeSet().containsAll(offices));
}
return stream;
}
protected static Stream<AcademicAccessRule> filter(AcademicOperationType function, Set<AcademicProgram> programs,
Set<AdministrativeOffice> offices, DateTime when) {
Stream<AcademicAccessRule> stream = filter(function, when);
if (programs != null && !programs.isEmpty()) {
stream = stream.filter(r -> r.getFullProgramSet().collect(Collectors.toSet()).containsAll(programs));
}
if (offices != null && !offices.isEmpty()) {
stream = stream.filter(r -> r.getOfficeSet().containsAll(offices));
}
return stream;
}
public static Stream<User> getMembers(Predicate<? super AcademicAccessRule> filter) {
return AcademicAccessRule.accessRules().filter(filter).map(AccessRule::getWhoCanAccess)
.flatMap(group -> group.getMembers());
}
public static Stream<User> getMembers(AcademicOperationType function, Set<AcademicProgram> programs,
Set<AdministrativeOffice> offices) {
return filter(function, programs, offices).map(AccessRule::getWhoCanAccess).flatMap(group -> group.getMembers());
}
public static Stream<User> getMembers(Predicate<? super AcademicAccessRule> filter, DateTime when) {
return AcademicAccessRule.accessRules(when).filter(filter).map(AccessRule::getWhoCanAccess)
.flatMap(group -> group.getMembers(when));
}
public static Stream<User> getMembers(AcademicOperationType function, Set<AcademicProgram> programs,
Set<AdministrativeOffice> offices, DateTime when) {
return filter(function, programs, offices, when).map(AccessRule::getWhoCanAccess)
.flatMap(group -> group.getMembers(when));
}
public static boolean isMember(User user, Predicate<? super AcademicAccessRule> filter) {
return AcademicAccessRule.accessRules().filter(filter).anyMatch(group -> group.isMember(user));
}
public static boolean isMember(User user, AcademicOperationType function, Set<AcademicProgram> programs,
Set<AdministrativeOffice> offices) {
return filter(function, programs, offices).anyMatch(group -> group.isMember(user));
}
public static boolean isMember(User user, Predicate<? super AcademicAccessRule> filter, DateTime when) {
return AcademicAccessRule.accessRules(when).filter(filter).anyMatch(group -> group.isMember(user, when));
}
public static boolean isMember(User user, AcademicOperationType function, Set<AcademicProgram> programs,
Set<AdministrativeOffice> offices, DateTime when) {
return filter(function, programs, offices, when).anyMatch(group -> group.isMember(user, when));
}
public static Stream<AcademicProgram> getProgramsAccessibleToFunction(AcademicOperationType function, User user) {
return filter(function).filter(r -> r.getWhoCanAccess().isMember(user)).flatMap(r -> r.getFullProgramSet());
}
public static boolean isProgramAccessibleToFunction(AcademicOperationType function, AcademicProgram program, User user) {
return filter(function).filter(r -> r.getWhoCanAccess().isMember(user)).flatMap(r -> r.getFullProgramSet())
.anyMatch(p -> p.equals(program));
}
public static Stream<Degree> getDegreesAccessibleToFunction(AcademicOperationType function, User user) {
return getProgramsAccessibleToFunction(function, user).filter(p -> p instanceof Degree).map(p -> (Degree) p);
}
public static Stream<PhdProgram> getPhdProgramsAccessibleToFunction(AcademicOperationType function, User user) {
return getProgramsAccessibleToFunction(function, user).filter(p -> p instanceof PhdProgram).map(p -> (PhdProgram) p);
}
public static Stream<DegreeType> getDegreeTypesAccessibleToFunction(AcademicOperationType function, User user) {
return getProgramsAccessibleToFunction(function, user).map(p -> p.getDegreeType()).filter(java.util.Objects::nonNull);
}
public static Stream<AdministrativeOffice> getOfficesAccessibleToFunction(AcademicOperationType function, User user) {
return filter(function).filter(r -> r.getWhoCanAccess().isMember(user)).flatMap(r -> r.getOfficeSet().stream());
}
@Override
public int compareTo(AcademicAccessRule o) {
int op = getOperation().compareTo(o.getOperation());
if (op != 0) {
return op;
}
int group = getWhoCanAccess().compareTo(o.getWhoCanAccess());
if (group != 0) {
return group;
}
return getExternalId().compareTo(o.getExternalId());
}
}