001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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    package org.kuali.kfs.sys.businessobject.lookup;
017    
018    import java.io.File;
019    import java.io.IOException;
020    import java.text.ParseException;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.Date;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Properties;
027    
028    import org.apache.commons.io.DirectoryWalker;
029    import org.apache.commons.io.IOCase;
030    import org.apache.commons.io.filefilter.AbstractFileFilter;
031    import org.apache.commons.io.filefilter.FileFilterUtils;
032    import org.apache.commons.io.filefilter.IOFileFilter;
033    import org.apache.commons.io.filefilter.WildcardFileFilter;
034    import org.apache.commons.lang.StringUtils;
035    import org.kuali.kfs.sys.batch.BatchFile;
036    import org.kuali.kfs.sys.batch.BatchFileUtils;
037    import org.kuali.kfs.sys.batch.service.BatchFileAdminAuthorizationService;
038    import org.kuali.rice.kns.bo.BusinessObject;
039    import org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl;
040    import org.kuali.rice.kns.lookup.HtmlData;
041    import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData;
042    import org.kuali.rice.kns.service.DateTimeService;
043    import org.kuali.rice.kns.util.DateUtils;
044    import org.kuali.rice.kns.util.GlobalVariables;
045    import org.kuali.rice.kns.util.KNSConstants;
046    import org.kuali.rice.kns.util.UrlFactory;
047    import org.kuali.rice.kns.web.ui.Field;
048    import org.kuali.rice.kns.web.ui.Row;
049    
050    public class BatchFileLookupableHelperServiceImpl extends AbstractLookupableHelperServiceImpl {
051        protected DateTimeService dateTimeService;
052        protected BatchFileAdminAuthorizationService batchFileAdminAuthorizationService;
053        
054        @Override
055        public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
056            List<BatchFile> results = new ArrayList<BatchFile>();
057    
058            IOFileFilter filter = FileFilterUtils.fileFileFilter();
059            
060            IOFileFilter pathBasedFilter = getPathBasedFileFilter();
061            if (pathBasedFilter != null) {
062                filter = FileFilterUtils.andFileFilter(filter, pathBasedFilter);
063            }
064            
065            String fileNamePattern = fieldValues.get("fileName");
066            IOFileFilter fileNameBasedFilter = getFileNameBasedFilter(fileNamePattern);
067            if (fileNameBasedFilter != null) {
068                filter = FileFilterUtils.andFileFilter(filter, fileNameBasedFilter);
069            }
070            
071            String lastModifiedDate = fieldValues.get("lastModifiedDate");
072            IOFileFilter lastModifiedDateBasedFilter = getLastModifiedDateBasedFilter(lastModifiedDate);
073            if (lastModifiedDateBasedFilter != null) {
074                filter = FileFilterUtils.andFileFilter(filter, lastModifiedDateBasedFilter);
075            }
076            
077            BatchFileFinder finder = new BatchFileFinder(results, filter);
078            List<File> rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories();
079            finder.find(rootDirectories);
080            
081            return results;
082        }
083    
084        protected IOFileFilter getPathBasedFileFilter() {
085            List<File> selectedFiles = getSelectedDirectories(getSelectedPaths());
086            if (selectedFiles.isEmpty()) {
087                return null;
088            }
089            IOFileFilter fileFilter = null;
090            for (File selectedFile : selectedFiles) {
091                IOFileFilter subFilter = new SubDirectoryFileFilter(selectedFile);
092                if (fileFilter == null) {
093                    fileFilter = subFilter;
094                }
095                else {
096                    fileFilter = FileFilterUtils.orFileFilter(fileFilter, subFilter);
097                }
098            }
099            return fileFilter;
100        }
101        
102        protected IOFileFilter getFileNameBasedFilter(String fileNamePattern) {
103            if (StringUtils.isNotBlank(fileNamePattern)) {
104                return new WildcardFileFilter(fileNamePattern, IOCase.INSENSITIVE);
105            }
106            return null;
107        }
108        
109        protected IOFileFilter getLastModifiedDateBasedFilter(String lastModifiedDatePattern) {
110            if (StringUtils.isBlank(lastModifiedDatePattern)) {
111                return null;
112            }
113            try {
114                if (lastModifiedDatePattern.startsWith("<=")) {
115                    String dateString = StringUtils.removeStart(lastModifiedDatePattern, "<=");
116                    Date toDate = dateTimeService.convertToDate(dateString);
117                    return new LastModifiedDateFileFilter(null, toDate);
118                }
119                if (lastModifiedDatePattern.startsWith(">=")) {
120                    String dateString = StringUtils.removeStart(lastModifiedDatePattern, ">=");
121                    Date fromDate = dateTimeService.convertToDate(dateString);
122                    return new LastModifiedDateFileFilter(fromDate, null);
123                }
124                if (lastModifiedDatePattern.contains("..")) {
125                    String[] dates = StringUtils.splitByWholeSeparator(lastModifiedDatePattern, "..", 2);
126                    Date fromDate = dateTimeService.convertToDate(dates[0]);
127                    Date toDate = dateTimeService.convertToDate(dates[1]);
128                    return new LastModifiedDateFileFilter(fromDate, toDate);
129                }
130            }
131            catch (ParseException e) {
132                throw new RuntimeException("Can't parse date", e);
133            }
134            throw new RuntimeException("Unable to perform search using last modified date " + lastModifiedDatePattern);
135        }
136        
137        protected List<File> getSelectedDirectories(String[] selectedPaths) {
138            List<File> directories = new ArrayList<File>();
139            if (selectedPaths != null) {
140                for (String selectedPath : selectedPaths) {
141                    File directory = new File(BatchFileUtils.resolvePathToAbsolutePath(selectedPath));
142                    if (!directory.exists()) {
143                        throw new RuntimeException("Non existent directory " + BatchFileUtils.resolvePathToAbsolutePath(selectedPath));
144                    }
145                    directories.add(directory);
146                }
147            }
148            return directories;
149        }
150        
151        protected String[] getSelectedPaths() {
152            List<Row> rows = getRows();
153            if (rows == null) {
154                return null;
155            }
156            for (Row row : rows) {
157                for (Field field : row.getFields()) {
158                    if ("path".equals(field.getPropertyName()) && Field.MULTISELECT.equals(field.getFieldType())) {
159                        String[] values = field.getPropertyValues();
160                        return values;
161                    }
162                }
163            }
164            return null;
165        }
166        
167        protected class SubDirectoryFileFilter extends AbstractFileFilter {
168            private File superDirectory;
169            
170            public SubDirectoryFileFilter(File superDirectory) {
171                this.superDirectory = superDirectory.getAbsoluteFile();
172            }
173            
174            @Override
175            public boolean accept(File file) {
176                file = file.getAbsoluteFile();
177                file = file.getParentFile();
178                while (file != null) {
179                    if (file.equals(superDirectory)) {
180                        return true;
181                    }
182                    file = file.getParentFile();
183                }
184                return false;
185            }
186        }
187        
188        protected class LastModifiedDateFileFilter extends AbstractFileFilter {
189            private Date fromDate;
190            private Date toDate;
191            
192            public LastModifiedDateFileFilter(Date fromDate, Date toDate) {
193                this.fromDate = fromDate;
194                this.toDate = toDate;
195            }
196            
197            @Override
198            public boolean accept(File file) {
199                Date lastModifiedDate = DateUtils.clearTimeFields(new Date(file.lastModified()));
200                
201                if (fromDate != null && fromDate.after(lastModifiedDate)) {
202                    return false;
203                }
204                if (toDate != null && toDate.before(lastModifiedDate)) {
205                    return false;
206                }
207                return true;
208            }
209        }
210        
211        protected class BatchFileFinder extends DirectoryWalker {
212            private List<BatchFile> results;
213            
214            public BatchFileFinder(List<BatchFile> results, IOFileFilter fileFilter) {
215                super(null, fileFilter, -1);
216                this.results = results;
217            }
218            
219            public void find(Collection<File> rootDirectories) {
220                try {
221                    for (File rootDirectory : rootDirectories) {
222                        walk(rootDirectory, null);
223                    }
224                }
225                catch (IOException e) {
226                    throw new RuntimeException("Error performing lookup", e);
227                }
228            }
229    
230            /**
231             * @see org.apache.commons.io.DirectoryWalker#handleFile(java.io.File, int, java.util.Collection)
232             */
233            @Override
234            protected void handleFile(File file, int depth, Collection results) throws IOException {
235                super.handleFile(file, depth, results);
236                BatchFile batchFile = new BatchFile();
237                batchFile.setFile(file);
238                this.results.add(batchFile);
239            }
240        }
241    
242        public void setDateTimeService(DateTimeService dateTimeService) {
243            this.dateTimeService = dateTimeService;
244        }
245    
246        @Override
247        public List<HtmlData> getCustomActionUrls(BusinessObject businessObject, List pkNames) {
248            List<HtmlData> links = new ArrayList<HtmlData>();
249            
250            BatchFile batchFile = (BatchFile) businessObject;
251            if (canDownloadFile(batchFile)) {
252                links.add(getDownloadUrl(batchFile));
253            }
254            if (canDeleteFile(batchFile)) {
255                links.add(getDeleteUrl(batchFile));
256            }
257            return links;
258        }
259    
260        protected boolean canDownloadFile(BatchFile batchFile) {
261            return batchFileAdminAuthorizationService.canDownload(batchFile, GlobalVariables.getUserSession().getPerson());
262        }
263    
264        protected boolean canDeleteFile(BatchFile batchFile) {
265            return batchFileAdminAuthorizationService.canDelete(batchFile, GlobalVariables.getUserSession().getPerson());
266        }
267        
268        protected HtmlData getDownloadUrl(BatchFile batchFile) {
269            Properties parameters = new Properties();
270            parameters.put("filePath", BatchFileUtils.pathRelativeToRootDirectory(batchFile.retrieveFile().getAbsolutePath()));
271            parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "download");
272            String href = UrlFactory.parameterizeUrl("../batchFileAdmin.do", parameters);
273            return new AnchorHtmlData(href, "download", "Download");
274        }
275        
276        protected HtmlData getDeleteUrl(BatchFile batchFile) {
277            Properties parameters = new Properties();
278            parameters.put("filePath", BatchFileUtils.pathRelativeToRootDirectory(batchFile.retrieveFile().getAbsolutePath()));
279            parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "delete");
280            String href = UrlFactory.parameterizeUrl("../batchFileAdmin.do", parameters);
281            return new AnchorHtmlData(href, "delete", "Delete");
282        }
283    
284        /**
285         * @see org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl#validateSearchParameters(java.util.Map)
286         */
287        @Override
288        public void validateSearchParameters(Map fieldValues) {
289            super.validateSearchParameters(fieldValues);
290            
291            String[] selectedPaths = getSelectedPaths();
292            if (selectedPaths != null) {
293                for (String selectedPath : selectedPaths) {
294                    String resolvedPath = BatchFileUtils.resolvePathToAbsolutePath(selectedPath);
295                    if (!BatchFileUtils.isDirectoryAccessible(resolvedPath)) {
296                        throw new RuntimeException("Can't access path " + selectedPath);
297                    }
298                }
299            }
300        }
301    
302        /**
303         * Sets the batchFileAdminAuthorizationService attribute value.
304         * @param batchFileAdminAuthorizationService The batchFileAdminAuthorizationService to set.
305         */
306        public void setBatchFileAdminAuthorizationService(BatchFileAdminAuthorizationService batchFileAdminAuthorizationService) {
307            this.batchFileAdminAuthorizationService = batchFileAdminAuthorizationService;
308        }
309    }