/** * * Copyright * 2009-2015 Jayway Products AB * 2016-2017 Föreningen Sambruk * * Licensed under AGPL, Version 3.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.gnu.org/licenses/agpl.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package se.streamsource.streamflow.web.context.services; import org.apache.pdfbox.exceptions.COSVisitorException; import org.apache.pdfbox.pdfwriter.COSWriter; import org.apache.pdfbox.pdmodel.PDDocument; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.injection.scope.Uses; import org.qi4j.api.specification.Specification; import org.qi4j.api.specification.Specifications; import org.qi4j.api.structure.Module; import org.qi4j.api.util.Function; import org.qi4j.api.util.Iterables; import org.qi4j.api.value.ValueBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import se.streamsource.dci.api.Role; import se.streamsource.dci.api.RoleMap; import se.streamsource.streamflow.api.administration.filter.ActionValue; import se.streamsource.streamflow.api.administration.filter.CloseActionValue; import se.streamsource.streamflow.api.administration.filter.EmailActionValue; import se.streamsource.streamflow.api.administration.filter.EmailNotificationActionValue; import se.streamsource.streamflow.api.administration.filter.FilterValue; import se.streamsource.streamflow.api.administration.filter.LabelRuleValue; import se.streamsource.streamflow.api.administration.filter.RuleValue; import se.streamsource.streamflow.api.workspace.cases.CaseOutputConfigDTO; import se.streamsource.streamflow.api.workspace.cases.conversation.MessageType; import se.streamsource.streamflow.util.Visitor; import se.streamsource.streamflow.web.application.defaults.SystemDefaultsService; import se.streamsource.streamflow.web.application.mail.HtmlMailGenerator; import se.streamsource.streamflow.web.application.mail.MailSender; import se.streamsource.streamflow.web.application.security.UserPrincipal; import se.streamsource.streamflow.web.context.workspace.cases.CaseCommandsContext; import se.streamsource.streamflow.web.domain.Describable; import se.streamsource.streamflow.web.domain.entity.caze.CaseEntity; import se.streamsource.streamflow.web.domain.entity.user.UserEntity; import se.streamsource.streamflow.web.domain.structure.attachment.Attachment; import se.streamsource.streamflow.web.domain.structure.conversation.Conversation; import se.streamsource.streamflow.web.domain.structure.conversation.ConversationParticipant; import se.streamsource.streamflow.web.domain.structure.conversation.Conversations; import se.streamsource.streamflow.web.domain.structure.conversation.MessageDraft; import se.streamsource.streamflow.web.domain.structure.group.Participant; import se.streamsource.streamflow.web.domain.structure.group.Participants; import se.streamsource.streamflow.web.domain.structure.label.Label; import se.streamsource.streamflow.web.domain.structure.project.filter.Filters; import se.streamsource.streamflow.web.domain.structure.user.User; import se.streamsource.streamflow.web.domain.structure.user.UserAuthentication; import se.streamsource.streamflow.web.infrastructure.attachment.AttachmentStore; import se.streamsource.streamflow.web.infrastructure.attachment.OutputstreamInput; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import static se.streamsource.streamflow.util.ForEach.*; /** * */ public class ApplyFilterContext { private FilterCheck filterCheck; private Filterable filterable; private Module module; private MailSender mailSender; private AttachmentStore attachmentStore; private ResourceBundle bundle; private HtmlMailGenerator htmlGenerator; private SystemDefaultsService systemDefaults; public ApplyFilterContext(@Structure Module module, @Uses MailSender mailSender, @Service AttachmentStore attachmentStore, @Service SystemDefaultsService systemDefaults) { this.module = module; this.mailSender = mailSender; this.attachmentStore = attachmentStore; this.filterCheck = new FilterCheck(); this.filterable = new Filterable(); this.htmlGenerator = module.objectBuilderFactory().newObject( HtmlMailGenerator.class ); this.bundle = ResourceBundle.getBundle(ApplyFilterContext.class.getName(), new Locale("SV", "se")); // TODO These texts need to use the locale for the user this.systemDefaults = systemDefaults; } public ApplyFilterContext rebind(Filters.Data filters, CaseEntity caze) { filterCheck.bind(filters); filterable.bind(caze); return this; } public void applyFilters() { filterCheck.applyFilters(filterable); } private class FilterCheck extends Role<Filters.Data> { public void applyFilters(final Filterable filterable) { for (FilterValue filterValue : forEach(self.filters().get()). filter(enabledFilter()). filter(filterTrigger().map(filterable))) { filterable.applyActions(filterValue.actions().get()); } } private Specification<FilterValue> enabledFilter() { return new Specification<FilterValue>() { public boolean satisfiedBy(FilterValue item) { return item.enabled().get(); } }; } private Function<Filterable, Specification<FilterValue>> filterTrigger() { return new Function<Filterable, Specification<FilterValue>>() { public Specification<FilterValue> map(final Filterable filterable) { return new Specification<FilterValue>() { public boolean satisfiedBy(FilterValue item) { List<Specification<Filterable>> specs = Iterables.addAll(new ArrayList<Specification<Filterable>>(), Iterables.map(new RuleFunction(), item.rules().get())); Specification<Filterable>[] specifications = specs.toArray((Specification<Filterable>[]) new Specification[specs.size()]); Specification<Filterable> specification; if (item.matching().get().equals(FilterValue.MatchingEnum.all)) { specification = Specifications.and(specifications); } else specification = Specifications.or(specifications); return specification.satisfiedBy(filterable); } }; } }; } class RuleFunction implements Function<RuleValue, Specification<Filterable>> { public Specification<Filterable> map(final RuleValue ruleValue) { if (ruleValue instanceof LabelRuleValue) { return new Specification<Filterable>() { public boolean satisfiedBy(Filterable item) { return item.hasLabel(((LabelRuleValue) ruleValue).label().get().identity()); } }; } return null; } } } private class Filterable extends Role<CaseEntity> { Logger logger = LoggerFactory.getLogger(ApplyFilterContext.class); private Filterable() { } public boolean hasLabel(String identity) { for (Label label : self.labels()) { if (label.toString().equals(identity)) return true; } return false; } public void applyActions(Iterable<ActionValue> actions) { for (ActionValue actionValue : actions) { // Run as Administrator RoleMap.newCurrentRoleMap(); RoleMap.current().set( module.unitOfWorkFactory().currentUnitOfWork().get( UserAuthentication.class, UserEntity.ADMINISTRATOR_USERNAME ) ); RoleMap.current().set( new UserPrincipal( UserEntity.ADMINISTRATOR_USERNAME ) ); /* * Have commented this piece of code since it's not available to the user in the adminview. * They where implemented from start by Rickard but have never been in use and has not been tested. * The usecase are rather complex and reuires some thinking in order to not mess up... * if (actionValue instanceof AssignActionValue) { Assignee assignee = module.unitOfWorkFactory().currentUnitOfWork().get(Assignee.class, ((AssignActionValue) actionValue).assignee().get().identity()); RoleMap.current().set(assignee); RoleMap.current().set(self); try { module.transientBuilderFactory().newTransient(CaseCommandsContext.class).assign(); } catch (Throwable e) { throw new IllegalStateException("Could not assign", e); } logger.info("Assigned " + self.caseId().get() + " to " + ((Describable) assignee).getDescription()); } else if (actionValue instanceof ChangeOwnerActionValue) { Owner owner = module.unitOfWorkFactory().currentUnitOfWork().get(Owner.class, ((ChangeOwnerActionValue) actionValue).sendTo().get().identity()); RoleMap.current().set(self); try { EntityValue to = module.valueBuilderFactory().newValueBuilder(EntityValue.class).prototype(); to.entity().set(((Identity) owner).identity().get()); module.transientBuilderFactory().newTransient(CaseCommandsContext.class).sendto(to); } catch (Throwable e) { throw new IllegalStateException("Could not change owner", e); } logger.info("Changed owner of " + self.caseId().get() + " to " + ((Describable) owner).getDescription()); } else */ RoleMap.current().set(self); if (actionValue instanceof CloseActionValue) { try { module.transientBuilderFactory().newTransient(CaseCommandsContext.class).close(); } catch (Throwable e) { throw new IllegalStateException("Could not close", e); } logger.info("Closed" + self.caseId().get()); } else if (actionValue instanceof EmailActionValue) { Participant participant = module.unitOfWorkFactory().currentUnitOfWork().get(Participant.class, ((EmailActionValue) actionValue).participant().get().identity()); createConversationMessageWithAttachments( RoleMap.role( UserEntity.class ), participant ); } else if (actionValue instanceof EmailNotificationActionValue) { Participant participant = module.unitOfWorkFactory().currentUnitOfWork().get(Participant.class, ((EmailNotificationActionValue) actionValue).participant().get().identity()); createNotificationConversation( RoleMap.role( UserEntity.class ), participant ); } } } private void createConversationMessageWithAttachments(UserEntity administrator, Participant participant) { Conversations conversations = RoleMap.role( Conversations.class ); Conversation conversation = conversations.createConversation( bundle.getString( "subject" ) + self.caseId().get(), administrator ); if( participant instanceof ConversationParticipant ) { conversation.addParticipant( (ConversationParticipant)participant ); } else if (participant instanceof Participants) { Participants.Data participants = (Participants.Data)participant; for (Participant participant1 : participants.participants()) { conversation.addParticipant( (ConversationParticipant)participant1 ); } } ((MessageDraft)conversation).changeDraftMessage( bundle.getString( "message" )); try { // Store case as PDF for attachment purposes ValueBuilder<CaseOutputConfigDTO> config = module.valueBuilderFactory().newValueBuilder(CaseOutputConfigDTO.class); config.prototype().attachments().set(true); config.prototype().contacts().set(true); config.prototype().conversations().set(true); config.prototype().submittedForms().set(true); config.prototype().caselog().set(true); RoleMap.current().set(new Locale( "SV", "se" )); RoleMap.current().set(self); final PDDocument pdf = module.transientBuilderFactory().newTransient(CaseCommandsContext.class).exportpdf(config.newInstance()); String id = attachmentStore.storeAttachment(new OutputstreamInput(new Visitor<OutputStream, IOException>() { public boolean visit(OutputStream out) throws IOException { COSWriter writer = new COSWriter(out); try { writer.write(pdf); } catch (COSVisitorException e) { throw new IOException(e); } finally { writer.close(); } return true; } }, 4096)); pdf.close(); System.out.println("Written to:" + id + ", length:" + attachmentStore.getAttachmentSize(id)); Attachment conversationAttachment = conversation.createAttachment( "store:" + id ); conversationAttachment.changeDescription( self.caseId().get() + ".pdf" ); conversationAttachment.changeMimeType( "application/pdf" ); conversationAttachment.changeModificationDate( self.createdOn().get() ); conversationAttachment.changeSize( attachmentStore.getAttachmentSize( id ) ); conversationAttachment.changeName( self.caseId().get() + ".pdf" ); if ( self.attachments().count() > 0 ) { for (Attachment caseAttachment : self.attachments()) { conversation.addAttachment( caseAttachment ); } } if( self.formAttachments().count() > 0 ) { for( Attachment formAttachment : self.formAttachments()) { conversation.addAttachment( formAttachment ); } } } catch (Throwable throwable) { logger.error("Could not create case notification message.", throwable); } conversation.createMessageFromDraft( administrator, MessageType.HTML ); } private void createNotificationConversation(UserEntity administrator, Participant participant) { Conversations conversations = RoleMap.role( Conversations.class ); Conversation conversation = conversations.createConversation( bundle.getString( "subject" ) + self.caseId().get(), administrator ); if( participant instanceof ConversationParticipant ) { conversation.addParticipant( (ConversationParticipant)participant ); } else if (participant instanceof Participants) { Participants.Data participants = (Participants.Data)participant; for (Participant participant1 : participants.participants()) { conversation.addParticipant( (ConversationParticipant)participant1 ); } } StringBuffer notification = new StringBuffer(); SimpleDateFormat dateFormat = new SimpleDateFormat( bundle.getString( "date_format" ) ); notification.append( bundle.getString( "description" )).append(": ").append(self.getDescription()).append("<BR>"); if (self.casepriority().get() != null) { notification.append( bundle.getString( "priority" )).append(": ").append(self.casepriority().get().getDescription()).append("<BR>"); } notification.append( bundle.getString( "createdon" )).append(": ").append( dateFormat.format( self.createdOn().get() )).append("<BR>"); if (self.dueOn().get() != null) { notification.append( bundle.getString( "duedate" )).append(": ").append( dateFormat.format( self.dueOn().get())).append("<BR>"); } if (self.createdBy().get() instanceof User) { notification.append( bundle.getString( "createdby" )).append(": ").append(((Describable)self.createdBy().get()).getDescription()).append("<BR>"); } if( self.owner().get() != null ) { notification.append( bundle.getString( "owner" )).append(": ").append(((Describable)self.owner().get()).getDescription()).append("<BR>"); } if( self.caseType().get() != null ) { notification.append( bundle.getString( "casetype" )).append(": ").append(self.caseType().get().getDescription()).append("<BR>"); } notification.append( bundle.getString( "labels" )).append(": "); boolean first = true; for (Label label : self.labels()) { if (!first) { notification.append(", "); } notification.append(label.getDescription()); first = false; } notification.append( "<BR>" ); // add link to case for the possibility to open by webclient notification.append( "<a href='" ); notification.append( systemDefaults.config().configuration().webclientBaseUrl() ); notification.append( self.identity().get() ); notification.append( "'>" ); notification.append( bundle.getString( "openlink" ) ); notification.append( "</a>"); notification.append( "<br>" ); ((MessageDraft)conversation).changeDraftMessage(notification.toString()); conversation.createMessageFromDraft( administrator, MessageType.HTML ); } } }