001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.user.server.logic;
018
019import com.echothree.model.control.order.server.control.OrderControl;
020import com.echothree.model.control.search.server.control.SearchControl;
021import com.echothree.model.control.user.server.control.UserControl;
022import com.echothree.model.control.workrequirement.server.logic.WorkRequirementLogic;
023import com.echothree.model.data.party.common.pk.PartyPK;
024import com.echothree.model.data.user.common.pk.UserVisitPK;
025import com.echothree.model.data.user.server.entity.UserVisit;
026import com.echothree.model.data.user.server.factory.UserVisitFactory;
027import com.echothree.util.server.control.BaseLogic;
028import com.echothree.util.server.persistence.EntityPermission;
029import javax.enterprise.context.ApplicationScoped;
030import javax.enterprise.inject.spi.CDI;
031import javax.inject.Inject;
032
033@ApplicationScoped
034public class UserVisitLogic
035        extends BaseLogic {
036
037    @Inject
038    UserVisitFactory userVisitFactory;
039
040    @Inject
041    OrderControl orderControl;
042
043    @Inject
044    SearchControl searchControl;
045
046    @Inject
047    UserControl userControl;
048
049    @Inject
050    UserSessionLogic userSessionLogic;
051
052    @Inject
053    WorkRequirementLogic workRequirementLogic;
054
055    protected UserVisitLogic() {
056        super();
057    }
058
059    public static UserVisitLogic getInstance() {
060        return CDI.current().select(UserVisitLogic.class).get();
061    }
062    
063    private void cleanupUserVisitDependencies(final UserVisit userVisit, final Long endTime, final PartyPK cleanedUpBy) {
064        orderControl.deleteOrderUserVisitsByUserVisit(userVisit);
065
066        // Clear any searches since they may contain results that should not be accessible by another user.
067        searchControl.removeUserVisitSearchesByUserVisit(userVisit);
068
069        workRequirementLogic.endWorkTimesByUserVisit(userVisit, endTime, cleanedUpBy);
070    }
071    
072    public void invalidateUserVisit(UserVisit userVisit, PartyPK invalidatedBy) {
073        if(userVisit != null) {
074            var userSession = userSessionLogic.deleteUserSessionByUserVisit(userVisit);
075            
076            cleanupUserVisitDependencies(userVisit, null, invalidatedBy);
077            
078            if(userVisit.getRetainUntilTime() == null) {
079                if(userSession != null) {
080                    userControl.removeUserSession(userSession);
081                }
082                
083                userControl.removeUserVisit(userVisit);
084            } else {
085                userControl.deleteUserVisit(userVisit);
086            }
087        }
088    }
089    
090    public void invalidateUserVisit(UserVisitPK userVisitPK, PartyPK invalidatedBy) {
091        invalidateUserVisit(userControl.getUserVisitByPKForUpdate(userVisitPK), invalidatedBy);
092    }
093    
094    public void invalidateAbandonedUserVisits(Long abandonedTime, PartyPK invalidatedBy) {
095        for(var userVisit: userControl.getAbandonedUserVisits(abandonedTime)) {
096            userVisit = userVisitFactory.getEntityFromPK(EntityPermission.READ_WRITE, userVisit.getPrimaryKey());
097            
098            invalidateUserVisit(userVisit, invalidatedBy);
099        }
100    }
101    
102    public void removeInvalidatedUserVisits() {
103        for(var userVisit: userControl.getInvalidatedUserVisits()) {
104            userVisit = userVisitFactory.getEntityFromPK(EntityPermission.READ_WRITE, userVisit.getPrimaryKey());
105            
106            userControl.removeUserVisit(userVisit);
107        }
108    }
109    
110    /** Disassociate any Party from the specified UserVisit by deleting the UserSession, keep all the Preferred* properties.
111     */
112    public void disassociatePartyFromUserVisit(final UserVisit userVisit, final Long endTime, final PartyPK disassociatedBy) {
113        var userSession = userControl.getUserSessionByUserVisitForUpdate(userVisit);
114        
115        if(userSession != null) {
116            // If there wasn't a specific UserSession and Party assocaited with the UserVisit,
117            // then we shouldn't need to perform this step.
118            cleanupUserVisitDependencies(userVisit, endTime, disassociatedBy);
119
120            userControl.deleteUserSession(userSession);
121        }
122    }
123    
124    /** Remove any Party's specific connection to the UserVisit and its UserKey. Clearing the connection in the UserKey is one step beyond
125     * what's provided by disassociatePartyFromUserVisit(...).
126     */
127    public void logout(final UserVisitPK userVisitPK, final Long endTime, final PartyPK loggedOutBy) {
128        var userVisit = userControl.getUserVisitByPK(userVisitPK);
129        var userKey = userVisit.getUserKey();
130        var userKeyDetailValue = userControl.getUserKeyDetailValueByPKForUpdate(userKey.getLastDetail().getPrimaryKey());
131
132        if(userKeyDetailValue.getPartyPK() != null || userKeyDetailValue.getPartyRelationshipPK() != null) {
133            userKeyDetailValue.setPartyPK(null);
134            userKeyDetailValue.setPartyRelationshipPK(null);
135            userControl.updateUserKeyFromValue(userKeyDetailValue);
136        }
137
138        disassociatePartyFromUserVisit(userVisit, endTime, loggedOutBy);
139
140        // TODO: Create audit trail
141    }
142    
143}