/**
*
* 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.domain.structure.form;
import static se.streamsource.dci.api.RoleMap.role;
import java.util.Date;
import java.util.List;
import org.qi4j.api.common.ConstructionException;
import org.qi4j.api.common.Optional;
import org.qi4j.api.common.UseDefaults;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.entity.Identity;
import org.qi4j.api.entity.Queryable;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.injection.scope.This;
import org.qi4j.api.mixin.Mixins;
import org.qi4j.api.property.Property;
import org.qi4j.api.specification.Specification;
import org.qi4j.api.structure.Module;
import org.qi4j.api.util.Iterables;
import org.qi4j.api.value.ValueBuilder;
import se.streamsource.dci.api.RoleMap;
import se.streamsource.streamflow.api.administration.form.AttachmentFieldValue;
import se.streamsource.streamflow.api.administration.form.CommentFieldValue;
import se.streamsource.streamflow.api.administration.form.GeoLocationFieldValue;
import se.streamsource.streamflow.api.administration.form.LocationDTO;
import se.streamsource.streamflow.api.administration.form.RequiredSignatureValue;
import se.streamsource.streamflow.api.workspace.cases.form.AttachmentFieldSubmission;
import se.streamsource.streamflow.api.workspace.cases.general.FieldSubmissionDTO;
import se.streamsource.streamflow.api.workspace.cases.general.FormDraftDTO;
import se.streamsource.streamflow.api.workspace.cases.general.PageSubmissionDTO;
import se.streamsource.streamflow.infrastructure.event.domain.DomainEvent;
import se.streamsource.streamflow.util.Strings;
import se.streamsource.streamflow.web.domain.entity.attachment.AttachmentEntity;
import se.streamsource.streamflow.web.domain.entity.form.FormEntity;
import se.streamsource.streamflow.web.domain.structure.SubmittedFieldValue;
import se.streamsource.streamflow.web.domain.structure.attachment.FormAttachments;
import se.streamsource.streamflow.web.domain.structure.caze.Location;
import se.streamsource.streamflow.web.domain.structure.organization.AccessPoint;
import se.streamsource.streamflow.web.domain.structure.user.ProxyUser;
import se.streamsource.streamflow.web.domain.structure.user.UserAuthentication;
import se.streamsource.streamflow.web.domain.util.FormVisibilityRuleValidator;
/**
* Maintains list of submitted forms on a case
*/
@Mixins(SubmittedForms.Mixin.class)
public interface SubmittedForms
{
SubmittedFormValue submitForm( FormDraft formSubmission, Submitter submitter );
/**
* Check if there are any submitted forms at all
*
* @return
*/
boolean hasSubmittedForms();
/**
* Find a given submitted form
*/
boolean hasUnreadForm();
/** Gets all submitted forms of the given kind of form.
*/
Iterable<SubmittedFormValue> getSubmittedFormValues(Form form);
void read( int index );
interface Data
{
@UseDefaults
@Queryable(false)
Property<List<SubmittedFormValue>> submittedForms();
}
interface Events
{
void submittedForm( @Optional DomainEvent event, SubmittedFormValue form );
void setUnread( @Optional DomainEvent event, int index );
}
abstract class Mixin
implements SubmittedForms, Events
{
@Structure
Module module;
@This
Data state;
@This
FormDrafts submissions;
@This
FormAttachments formAttachments;
public SubmittedFormValue submitForm( FormDraft formSubmission, Submitter submitter )
{
FormDraftDTO DTO = formSubmission.getFormDraftValue();
FormVisibilityRuleValidator visibilityValidator = module.objectBuilderFactory()
.newObjectBuilder( FormVisibilityRuleValidator.class ).use( DTO ).newInstance();
ValueBuilder<SubmittedFormValue> formBuilder = module.valueBuilderFactory().newValueBuilder(SubmittedFormValue.class);
formBuilder.prototype().submitter().set( EntityReference.getEntityReference(submitter) );
formBuilder.prototype().form().set( DTO.form().get() );
formBuilder.prototype().submissionDate().set( new Date() );
ValueBuilder<SubmittedFieldValue> fieldBuilder = module.valueBuilderFactory().newValueBuilder(SubmittedFieldValue.class);
for (PageSubmissionDTO pageDTO : DTO.pages().get())
{
ValueBuilder<SubmittedPageValue> pageBuilder = module.valueBuilderFactory().newValueBuilder(SubmittedPageValue.class);
pageBuilder.prototype().page().set(pageDTO.page().get());
// omit invisible pages.
if( !visibilityValidator.visible( pageDTO ) )
continue;
for (FieldSubmissionDTO field : pageDTO.fields().get())
{
// ignore comment fields when submitting
if ( !(field.field().get().fieldValue().get() instanceof CommentFieldValue) )
{
if(!visibilityValidator.visible(field))
{
continue;
}
// Is mandatory field missing?
if (field.field().get().mandatory().get() )
{
if( visibilityValidator.visible( field ) && Strings.empty( field.value().get() ) )
throw new IllegalArgumentException( "mandatory_value_missing" );
}
// Validate
if (field.field().get() != null && field.value().get() != null && !field.field().get().fieldValue().get().validate( field.value().get() ))
throw new IllegalArgumentException( "invalid_value" );
fieldBuilder.prototype().field().set( field.field().get().field().get() );
if ( field.value().get() == null )
{
fieldBuilder.prototype().value().set( "" );
} else {
fieldBuilder.prototype().value().set(field.value().get());
}
// SF-846 from now on we save original FieldValue layout with the submission to be able to
// provide the same replacement SelectionFieldValue elements that are possibly provided by
// an access point.
fieldBuilder.prototype().origFieldValue().set( field.field().get().fieldValue().get() );
// move current attachment from draft to case
if( field.field().get().fieldValue().get() instanceof AttachmentFieldValue )
{
try
{
AttachmentFieldSubmission currentFormDraftAttachmentField = module.valueBuilderFactory().newValueFromJSON(AttachmentFieldSubmission.class, fieldBuilder.prototype().value().get());
AttachmentEntity attachment = module.unitOfWorkFactory().currentUnitOfWork().get( AttachmentEntity.class, currentFormDraftAttachmentField.attachment().get().identity() );
((FormAttachments)formSubmission).moveAttachment( formAttachments, attachment );
} catch (ConstructionException e)
{
// ignore
}
}
// Move Location from draft to Case
if (field.field().get().fieldValue().get() instanceof GeoLocationFieldValue )
{
if (field.value().get() != null)
{
Location location = RoleMap.role( Location.class );
LocationDTO locationValue = module.valueBuilderFactory().newValueFromJSON( LocationDTO.class, field.value().get() );
location.changeLocation( locationValue.location().get() );
location.changeStreet( locationValue.street().get() );
location.changeZipcode( locationValue.zipcode().get() );
location.changeCity( locationValue.city().get() );
location.changeCity( locationValue.country().get() );
}
// SF-867 Make sure that field value is not an empty string - it must at least be a set of curly braces!!
// Otherwise newValueFromJSON on the way back to the client gets an ParseException
if( "".equals( fieldBuilder.prototype().value().get() ) )
{
fieldBuilder.prototype().value().set( "{}" );
}
}
pageBuilder.prototype().fields().get().add( fieldBuilder.newInstance() );
}
}
formBuilder.prototype().pages().get().add(pageBuilder.newInstance());
}
// Check for active signatures, catch and ignore IllegalArgumentException if we do not have a role AccessPoint
// in that case we are coming from the clients form wizard!!
AccessPoint accessPoint = null;
try
{
accessPoint= role( AccessPoint.class );
} catch( IllegalArgumentException ia )
{
// do nothing - this approach is used to determine if we are coming from Surface Webforms or from client form wizard.
}
if( accessPoint != null )
{
RequiredSignatures.Data requiredSignatures = module.unitOfWorkFactory().currentUnitOfWork().get( RequiredSignatures.Data.class, ((Identity) accessPoint).identity().get() );
Iterable<RequiredSignatureValue> activeSignatures = Iterables.filter( new Specification<RequiredSignatureValue>()
{
public boolean satisfiedBy( RequiredSignatureValue signature )
{
return signature.active().get();
}
}, requiredSignatures.requiredSignatures().get() );
// set second signee if we expect one
if ( Iterables.count( activeSignatures ) > 1 )
{
formBuilder.prototype().secondsignee().set( DTO.secondsignee().get() );
}
// Transfer signatures
formBuilder.prototype().signatures().get().addAll(DTO.signatures().get());
}
// Mark form submission as unread if it comes from WebForms
UserAuthentication user = RoleMap.role( UserAuthentication.class );
if( user instanceof ProxyUser )
{
formBuilder.prototype().unread().set( true );
}
SubmittedFormValue submittedForm = formBuilder.newInstance();
submittedForm( null, submittedForm );
// Now discard it
submissions.discardFormDraft( formSubmission );
return submittedForm;
}
public void submittedForm( @Optional DomainEvent event, SubmittedFormValue form )
{
List<SubmittedFormValue> forms = state.submittedForms().get();
forms.add( form );
state.submittedForms().set( forms );
}
public boolean hasSubmittedForms()
{
return !state.submittedForms().get().isEmpty();
}
public boolean hasUnreadForm()
{
return Iterables.matchesAny( new Specification<SubmittedFormValue>()
{
public boolean satisfiedBy( SubmittedFormValue submittedForm )
{
return submittedForm.unread().get();
}
}, state.submittedForms().get() );
}
@Override
public Iterable<SubmittedFormValue> getSubmittedFormValues(final Form form) {
return Iterables.filter(new Specification<SubmittedFormValue>() {
@Override
public boolean satisfiedBy(SubmittedFormValue item) {
return item.form().get().identity().equals(((FormEntity) form).identity().get());
}
}, state.submittedForms().get());
}
public void read( int index )
{
setUnread( null, index );
}
public void setUnread( DomainEvent event, int index )
{
List<SubmittedFormValue> forms = state.submittedForms().get();
SubmittedFormValue form = forms.get( index );
int count = 0;
for( SubmittedFormValue value : state.submittedForms().get() )
{
if( form.form().equals( value.form() ))
{
ValueBuilder<SubmittedFormValue> builder = module.valueBuilderFactory().newValueBuilder( SubmittedFormValue.class );
builder.withPrototype( forms.get( count ) ).prototype().unread().set( false );
forms.set( count, builder.newInstance() );
}
count++;
}
state.submittedForms().set( forms );
}
}
}