=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java 2014-10-23 18:10:58 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java 2014-10-24 10:01:14 +0000 @@ -43,6 +43,8 @@ private boolean mayAccept; private boolean mayUnaccept; + + private boolean mayReadData; private transient String state; @@ -95,6 +97,17 @@ } @JsonProperty + public boolean isMayReadData() + { + return mayReadData; + } + + public void setMayReadData( boolean mayReadData ) + { + this.mayReadData = mayReadData; + } + + @JsonProperty public String getState() { return state; === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java 2014-10-23 21:47:57 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java 2014-10-24 10:01:14 +0000 @@ -109,8 +109,8 @@ * category option combos that the user is allowed to see. * * @param dataSets DataSets that we are getting the status for - * @param period period we are getting the status for + * @param periods Periods we are getting the status for * @return list of status and permissions */ - List getUserDataApprovalsAndPermissions( Set dataSets, Period period ); + List getUserDataApprovalsAndPermissions( Set dataSets, Set periods ); } === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java 2014-10-23 10:07:41 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java 2014-10-24 10:01:14 +0000 @@ -33,6 +33,8 @@ import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.Period; +import java.util.Set; + /** * Defines the functionality for persisting DataApproval objects. * @@ -81,4 +83,16 @@ */ DataApproval getDataApproval( DataApprovalLevel dataApprovalLevel, DataSet dataSet, Period period, OrganisationUnit organisationUnit, DataElementCategoryOptionCombo attributeOptionCombo ); + + /** + * Returns a set of DataApproval objects representing the approval states + * for organisation units & category option combos that the user is allowed + * to see. + * + * @param dataSets Data sets to look within + * @param periods Periods to look within + * @return data approval objects for the user to see + */ + Set getUserDataApprovals( Set dataSets, Set periods); + } === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java 2014-10-23 21:47:57 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java 2014-10-24 10:01:14 +0000 @@ -366,7 +366,57 @@ } @Override - public List getUserDataApprovalsAndPermissions( Set dataSets, Period period ) + public List getUserDataApprovalsAndPermissions( Set dataSets, Set periods ) + { + User user = currentUserService.getCurrentUser(); + + boolean authorizedToApprove = user.getUserCredentials().isAuthorized( DataApproval.AUTH_APPROVE ); + boolean authorizedToApproveAtLowerLevels = user.getUserCredentials().isAuthorized( DataApproval.AUTH_APPROVE_LOWER_LEVELS ); + boolean authorizedToAcceptAtLowerLevels = user.getUserCredentials().isAuthorized( DataApproval.AUTH_ACCEPT_LOWER_LEVELS ); + + int maxApprovalLevel = dataApprovalLevelService.getAllDataApprovalLevels().size(); + + List statusList = new ArrayList<>(); + + for ( DataApproval da : dataApprovalStore.getUserDataApprovals( dataSets, periods ) ) + { + DataApprovalPermissions permissions = new DataApprovalPermissions(); + + if ( da.getOrganisationUnit() != null ) //TODO: Shouldn't be null -- fix the category option mappings to org units in the database. + { + DataApprovalLevel userApprovalLevel = dataApprovalLevelService.getUserApprovalLevel( da.getOrganisationUnit(), false ); + + if ( userApprovalLevel != null ) + { + int userLevel = userApprovalLevel.getLevel(); + int dataLevel = da.getDataApprovalLevel() == null ? maxApprovalLevel + 1 : da.getDataApprovalLevel().getLevel(); + + boolean mayApprove = ( authorizedToApprove && userLevel == dataLevel && !da.isAccepted() ) || + authorizedToApproveAtLowerLevels && userLevel < dataLevel; + + boolean mayAcceptOrUnaccept = authorizedToAcceptAtLowerLevels && dataLevel <= maxApprovalLevel && + ( userLevel == dataLevel + 1 || ( userLevel < dataLevel && authorizedToApproveAtLowerLevels ) ); + + boolean mayUnapprove = mayApprove && ( !da.isAccepted() || mayAcceptOrUnaccept ); + + permissions.setMayApprove( mayApprove ); + permissions.setMayUnapprove( mayUnapprove ); + permissions.setMayAccept( mayAcceptOrUnaccept ); + permissions.setMayUnaccept( mayAcceptOrUnaccept ); + } + boolean mayReadData = true; //TODO: Fix. + + permissions.setMayReadData( mayReadData ); + + statusList.add( new DataApprovalStatus( null, da, da.getDataApprovalLevel(), permissions ) ); + } + } + + return statusList; + } + + /* + public List getUserDataApprovalsAndPermissions( Set dataSets, Set periods ) { tracePrint( "---------------------------------------------------------------------- getUserDataApprovalsAndPermissions" ); @@ -390,6 +440,7 @@ return statusList; } + */ // ------------------------------------------------------------------------- // Supportive methods === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java 2014-10-21 19:45:22 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java 2014-10-24 10:01:14 +0000 @@ -32,13 +32,29 @@ import org.hibernate.criterion.Restrictions; import org.hisp.dhis.dataapproval.DataApproval; import org.hisp.dhis.dataapproval.DataApprovalLevel; +import org.hisp.dhis.dataapproval.DataApprovalLevelService; import org.hisp.dhis.dataapproval.DataApprovalStore; +import org.hisp.dhis.dataelement.DataElementCategoryCombo; import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; +import org.hisp.dhis.dataelement.DataElementCategoryService; import org.hisp.dhis.dataset.DataSet; import org.hisp.dhis.hibernate.HibernateGenericStore; import org.hisp.dhis.organisationunit.OrganisationUnit; +import org.hisp.dhis.organisationunit.OrganisationUnitService; import org.hisp.dhis.period.Period; import org.hisp.dhis.period.PeriodService; +import org.hisp.dhis.system.util.ConversionUtils; +import org.hisp.dhis.system.util.DateUtils; +import org.hisp.dhis.system.util.MathUtils; +import org.hisp.dhis.system.util.TextUtils; +import org.hisp.dhis.user.CurrentUserService; +import org.hisp.dhis.user.User; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.rowset.SqlRowSet; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; /** * @author Jim Grace @@ -51,6 +67,13 @@ // Dependencies // ------------------------------------------------------------------------- + private JdbcTemplate jdbcTemplate; + + public void setJdbcTemplate( JdbcTemplate jdbcTemplate ) + { + this.jdbcTemplate = jdbcTemplate; + } + private PeriodService periodService; public void setPeriodService( PeriodService periodService ) @@ -58,6 +81,34 @@ this.periodService = periodService; } + private CurrentUserService currentUserService; + + public void setCurrentUserService( CurrentUserService currentUserService ) + { + this.currentUserService = currentUserService; + } + + private OrganisationUnitService organisationUnitService; + + public void setOrganisationUnitService( OrganisationUnitService organisationUnitService ) + { + this.organisationUnitService = organisationUnitService; + } + + private DataElementCategoryService categoryService; + + public void setCategoryService( DataElementCategoryService categoryService ) + { + this.categoryService = categoryService; + } + + private DataApprovalLevelService dataApprovalLevelService; + + public void setDataApprovalLevelService( DataApprovalLevelService dataApprovalLevelService ) + { + this.dataApprovalLevelService = dataApprovalLevelService; + } + // ------------------------------------------------------------------------- // DataApproval // ------------------------------------------------------------------------- @@ -101,4 +152,175 @@ return (DataApproval) criteria.uniqueResult(); } + + @Override + public Set getUserDataApprovals( Set dataSets, Set periods) + { + Date minDate = null; + Date maxDate = null; + + for ( Period p : periods ) + { + if ( minDate == null || p.getStartDate().before( minDate ) ) + { + minDate = p.getStartDate(); + } + if ( maxDate == null || p.getEndDate().after( maxDate ) ) + { + maxDate = p.getEndDate(); + } + } + + String sPeriods = ""; + + for ( Period p : periods ) + { + sPeriods += ( sPeriods.isEmpty() ? "" : ", " ) + periodService.reloadPeriod( p ).getId(); + } + + Set categoryComboIds = new HashSet<>(); + + for ( DataSet ds : dataSets ) + { + categoryComboIds.add( ds.getCategoryCombo().getId() ); + } + + String sDataSetCCs = TextUtils.getCommaDelimitedString( categoryComboIds ); + + String limitCategoryOptionByOrgUnit = ""; + String limitApprovalByOrgUnit = ""; + + for ( OrganisationUnit orgUnit : currentUserService.getCurrentUser().getOrganisationUnits() ) + { + if ( orgUnit.getParent() == null ) // User has root org unit access + { + limitCategoryOptionByOrgUnit = ""; + limitApprovalByOrgUnit = ""; + break; + } + + int level = organisationUnitService.getLevelOfOrganisationUnit( orgUnit ); + limitCategoryOptionByOrgUnit += "ous.idlevel" + level + " = " + orgUnit.getId() + " or "; + limitApprovalByOrgUnit += "ousda.idlevel" + level + " = " + orgUnit.getId() + " or "; + } + + if ( !limitCategoryOptionByOrgUnit.isEmpty() ) + { + limitCategoryOptionByOrgUnit = "and (" + limitCategoryOptionByOrgUnit + "coo.categoryoptionid is null) "; + limitApprovalByOrgUnit = "and (" + limitApprovalByOrgUnit + "ousda.organisationunitid is null) "; + } + + String limitBySharing = ""; + + if ( !currentUserService.currentUserIsSuper() ) + { + limitBySharing = "and (ugm.userid = " + currentUserService.getCurrentUser().getId() + " or left(co.publicaccess,1) = 'r') "; + } + + String sql = "select ccoc.categoryoptioncomboid, da.periodid, dal.level, coo.organisationunitid, da.accepted " + + "from categorycombos_optioncombos ccoc " + + "join categoryoptioncombos_categoryoptions cocco on cocco.categoryoptioncomboid = ccoc.categoryoptioncomboid " + + "join dataelementcategoryoption co on co.categoryoptionid = cocco.categoryoptionid " + + "left outer join cateogryoption_organisationunits coo on coo.categoryoptionid = cocco.categoryoptionid " + + "left outer join _orgunitstructure ous on ous.organisationunitid = coo.organisationunitid " + + "left outer join dataelementcategoryoptionusergroupaccesses couga on couga.categoryoptionid = cocco.categoryoptionid " + + "left outer join usergroupaccess uga on uga.usergroupaccessid = couga.usergroupaccessid " + + "left outer join usergroupmembers ugm on ugm.usergroupid = uga.usergroupid " + + "left outer join dataapproval da on da.categoryoptioncomboid = ccoc.categoryoptioncomboid and da.periodid in (" + sPeriods + ") " + + "left outer join dataapprovallevel dal on dal.dataapprovallevelid = da.dataapprovallevelid " + + "left outer join _orgunitstructure ousda on ousda.organisationunitid = da.organisationunitid " + + "where ccoc.categorycomboid in (" + sDataSetCCs + ") " + + "and (co.startdate is null or co.startdate <= '" + DateUtils.getMediumDateString( maxDate ) + "') " + + "and (co.enddate is null or co.enddate >= '" + DateUtils.getMediumDateString( minDate ) + "') " + + limitCategoryOptionByOrgUnit + + limitApprovalByOrgUnit + + limitBySharing + + "group by ccoc.categoryoptioncomboid, da.periodid, dal.level, coo.organisationunitid, da.accepted " + + "order by ccoc.categoryoptioncomboid, da.periodid, dal.level"; + + System.out.println( "sql = " + sql ); + SqlRowSet rowSet = jdbcTemplate.queryForRowSet( sql ); + + int previousAttributeOptionComboId = 0; + int previousPeriodId = 0; + int previousLevel = 0; + + Set userDataApprovals = new HashSet<>(); + + while ( rowSet.next() ) + { + Integer attributeOptionComboId = rowSet.getInt( 1 ); + Integer periodId = rowSet.getInt( 2 ); + Integer level = rowSet.getInt( 3 ); + Integer orgUnitId = rowSet.getInt( 4 ); + Boolean accepted = rowSet.getBoolean( 5 ); + + if ( attributeOptionComboId == previousAttributeOptionComboId && periodId == previousPeriodId && level > previousLevel ) + { + continue; + } + + previousAttributeOptionComboId = attributeOptionComboId; + previousPeriodId = periodId; + previousLevel = level; + + DataElementCategoryOptionCombo attributeOptionCombo = categoryService.getDataElementCategoryOptionCombo( attributeOptionComboId ); + Period period = periodService.getPeriod( periodId ); + DataApprovalLevel dataApprovalLevel = ( level == null ? null : dataApprovalLevelService.getDataApprovalLevelByLevelNumber( level ) ); + OrganisationUnit orgUnit = ( orgUnitId == null ? null : organisationUnitService.getOrganisationUnit( orgUnitId ) ); + + //TODO: currently special cased for PEFPAR's requirements. Can we make it more generic? + if ( level > 1 && attributeOptionCombo.equals ( categoryService.getDefaultDataElementCategoryOptionCombo() ) ) + { + for ( OrganisationUnit ou : getUserOrgsAtLevel( 3 ) ) + { + DataApproval da = new DataApproval( dataApprovalLevel, null, period, ou, attributeOptionCombo, accepted, null, null ); + + userDataApprovals.add( da ); + } + + continue; + } + DataApproval da = new DataApproval( dataApprovalLevel, null, period, orgUnit, attributeOptionCombo, accepted, null, null ); + + userDataApprovals.add( da ); + } + + return userDataApprovals; + } + + // ------------------------------------------------------------------------- + // Supportive methods + // ------------------------------------------------------------------------- + + private Set getUserOrgsAtLevel( int desiredLevel ) + { + Set orgUnits = new HashSet<>(); + + for ( OrganisationUnit orgUnit : currentUserService.getCurrentUser().getOrganisationUnits() ) + { + orgUnits.addAll( getOrgsAtLevel( orgUnit, desiredLevel, organisationUnitService.getLevelOfOrganisationUnit( orgUnit ) ) ); + } + + return orgUnits; + } + + private Set getOrgsAtLevel( OrganisationUnit orgUnit, int desiredLevel, int thisLevel ) + { + Set orgUnits = new HashSet<>(); + + if ( thisLevel < desiredLevel ) + { + for ( OrganisationUnit child : orgUnit.getChildren() ) + { + orgUnits.addAll( getOrgsAtLevel( child, desiredLevel, thisLevel + 1 ) ); + } + } + else if ( thisLevel == desiredLevel ) + { + orgUnits.add( orgUnit ); + } + + return orgUnits; + } } === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml' --- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-10-23 10:07:41 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-10-24 10:01:14 +0000 @@ -71,8 +71,13 @@ + + + + + === modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java' --- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java 2014-10-23 21:47:57 +0000 +++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java 2014-10-24 10:01:14 +0000 @@ -262,7 +262,7 @@ return; } - List statusList = dataApprovalService.getUserDataApprovalsAndPermissions( dataSets, period ); + List statusList = dataApprovalService.getUserDataApprovalsAndPermissions( dataSets, asSet( period ) ); List> list = new ArrayList<>();