From: <udi...@us...> - 2021-10-27 15:30:49
|
Revision: 1404 http://sourceforge.net/p/j-trac/code/1404 Author: udittmer Date: 2021-10-27 15:30:46 +0000 (Wed, 27 Oct 2021) Log Message: ----------- added the {U+201C}link configuration{U+201D} feature from the https://github.com/LibreNico/jtrac fork Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/Jtrac.java trunk/jtrac/src/main/java/info/jtrac/JtracDao.java trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java trunk/jtrac/src/main/java/info/jtrac/hibernate/HibernateJtracDao.java trunk/jtrac/src/main/java/info/jtrac/wicket/DashboardPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/DashboardPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/IndividualHeadPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/OptionsPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/OptionsPage.java trunk/jtrac/src/main/resources/jtrac.hbm.xml trunk/jtrac/src/main/resources/messages.properties trunk/jtrac/src/main/resources/messages_de.properties trunk/jtrac/src/main/resources/messages_en.properties Added Paths: ----------- trunk/jtrac/src/main/java/info/jtrac/domain/StoredSearch.java trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchListPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchListPage.java Modified: trunk/jtrac/src/main/java/info/jtrac/Jtrac.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/Jtrac.java 2021-10-27 08:20:04 UTC (rev 1403) +++ trunk/jtrac/src/main/java/info/jtrac/Jtrac.java 2021-10-27 15:30:46 UTC (rev 1404) @@ -1,124 +1,128 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 - * - * 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 info.jtrac; - -import info.jtrac.domain.BatchInfo; -import info.jtrac.domain.Config; -import info.jtrac.domain.Counts; -import info.jtrac.domain.CountsHolder; -import info.jtrac.domain.Field; -import info.jtrac.domain.History; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemItem; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.Metadata; -import info.jtrac.domain.Space; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; - -import java.util.List; -import java.util.Map; - -import org.acegisecurity.userdetails.UserDetailsService; -import org.apache.wicket.markup.html.form.upload.FileUpload; - -/** - * Jtrac main business interface (Service Layer) - */ -public interface Jtrac extends UserDetailsService { - - // TODO remove Wicket dep with FileUpload - void storeItem(Item item, FileUpload fileUpload); - void storeItems(List<Item> items); - void updateItem(Item item, User user); - void storeHistoryForItem(long itemId, History history, FileUpload fileUpload); - Item loadItem(long id); - Item loadItemByRefId(String refId); - History loadHistory(long id); - List<Item> findItems(ItemSearch itemSearch); - int loadCountOfAllItems(); - List<Item> findAllItems(int firstResult, int batchSize); - void removeItem(Item item); - void removeItemItem(ItemItem itemItem); - //======================================================== - int loadCountOfRecordsHavingFieldNotNull(Space space, Field field); - int bulkUpdateFieldToNull(Space space, Field field); - int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey); - int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey); - int loadCountOfRecordsHavingStatus(Space space, int status); - int bulkUpdateStatusToOpen(Space space, int status); - int bulkUpdateRenameSpaceRole(Space space, String oldRoleKey, String newRoleKey); - int bulkUpdateDeleteSpaceRole(Space space, String roleKey); - //======================================================== - void storeUser(User user); - void storeUser(User user, String password, boolean sendNotifications); - void removeUser(User user); - User loadUser(long id); - User loadUser(String loginName); - List<User> findAllUsers(); - List<User> findUsersWhereIdIn(List<Long> ids); - List<User> findUsersMatching(String searchText, String searchOn); - List<User> findUsersForSpace(long spaceId); - List<UserSpaceRole> findUserRolesForSpace(long spaceId); - Map<Long, List<UserSpaceRole>> loadUserRolesMapForSpace(long spaceId); - Map<Long, List<UserSpaceRole>> loadSpaceRolesMapForUser(long userId); - List<User> findUsersWithRoleForSpace(long spaceId, String roleKey); - List<User> findUsersForUser(User user); - List<User> findUsersNotFullyAllocatedToSpace(long spaceId); - int loadCountOfHistoryInvolvingUser(User user); - //======================================================== - CountsHolder loadCountsForUser(User user); - Counts loadCountsForUserSpace(User user, Space space); - //======================================================== - void storeSpace(Space space); - Space loadSpace(long id); - Space loadSpace(String prefixCode); - List<Space> findAllSpaces(); - List<Space> findSpacesWhereIdIn(List<Long> ids); - List<Space> findSpacesWhereGuestAllowed(); - List<Space> findSpacesNotFullyAllocatedToUser(long userId); - void removeSpace(Space space); - //======================================================== - void storeUserSpaceRole(User user, Space space, String roleKey); - UserSpaceRole loadUserSpaceRole(long id); - void removeUserSpaceRole(UserSpaceRole userSpaceRole); - //======================================================== - void storeMetadata(Metadata metadata); - Metadata loadMetadata(long id); - //======================================================== - String generatePassword(); - String encodeClearText(String clearText); - Map<String, String> getLocales(); - String getDefaultLocale(); - String getJtracHome(); - int getAttachmentMaxSizeInMb(); - int getSessionTimeoutInMinutes(); - //======================================================== - Map<String, String> loadAllConfig(); - void storeConfig(Config config); - String loadConfig(String param); - //======================================================== - void rebuildIndexes(BatchInfo batchInfo); - boolean validateTextSearchQuery(String text); - //======================================================== - void executeHourlyTask(); - void executePollingTask(); - //======================================================== - String getReleaseVersion(); - String getReleaseTimestamp(); - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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 info.jtrac; + +import info.jtrac.domain.BatchInfo; +import info.jtrac.domain.Config; +import info.jtrac.domain.Counts; +import info.jtrac.domain.CountsHolder; +import info.jtrac.domain.Field; +import info.jtrac.domain.History; +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemItem; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.Metadata; +import info.jtrac.domain.Space; +import info.jtrac.domain.StoredSearch; +import info.jtrac.domain.User; +import info.jtrac.domain.UserSpaceRole; + +import java.util.List; +import java.util.Map; + +import org.acegisecurity.userdetails.UserDetailsService; +import org.apache.wicket.markup.html.form.upload.FileUpload; + +/** + * Jtrac main business interface (Service Layer) + */ +public interface Jtrac extends UserDetailsService { + + // TODO remove Wicket dep with FileUpload + void storeItem(Item item, FileUpload fileUpload); + void storeItems(List<Item> items); + void updateItem(Item item, User user); + void storeHistoryForItem(long itemId, History history, FileUpload fileUpload); + Item loadItem(long id); + Item loadItemByRefId(String refId); + History loadHistory(long id); + List<Item> findItems(ItemSearch itemSearch); + int loadCountOfAllItems(); + List<Item> findAllItems(int firstResult, int batchSize); + void removeItem(Item item); + void removeItemItem(ItemItem itemItem); + //======================================================== + int loadCountOfRecordsHavingFieldNotNull(Space space, Field field); + int bulkUpdateFieldToNull(Space space, Field field); + int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey); + int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey); + int loadCountOfRecordsHavingStatus(Space space, int status); + int bulkUpdateStatusToOpen(Space space, int status); + int bulkUpdateRenameSpaceRole(Space space, String oldRoleKey, String newRoleKey); + int bulkUpdateDeleteSpaceRole(Space space, String roleKey); + //======================================================== + void storeUser(User user); + void storeUser(User user, String password, boolean sendNotifications); + void removeUser(User user); + User loadUser(long id); + User loadUser(String loginName); + List<User> findAllUsers(); + List<User> findUsersWhereIdIn(List<Long> ids); + List<User> findUsersMatching(String searchText, String searchOn); + List<User> findUsersForSpace(long spaceId); + List<UserSpaceRole> findUserRolesForSpace(long spaceId); + Map<Long, List<UserSpaceRole>> loadUserRolesMapForSpace(long spaceId); + Map<Long, List<UserSpaceRole>> loadSpaceRolesMapForUser(long userId); + List<User> findUsersWithRoleForSpace(long spaceId, String roleKey); + List<User> findUsersForUser(User user); + List<User> findUsersNotFullyAllocatedToSpace(long spaceId); + int loadCountOfHistoryInvolvingUser(User user); + //======================================================== + CountsHolder loadCountsForUser(User user); + Counts loadCountsForUserSpace(User user, Space space); + //======================================================== + void storeSpace(Space space); + Space loadSpace(long id); + Space loadSpace(String prefixCode); + List<Space> findAllSpaces(); + List<Space> findSpacesWhereIdIn(List<Long> ids); + List<Space> findSpacesWhereGuestAllowed(); + List<Space> findSpacesNotFullyAllocatedToUser(long userId); + void removeSpace(Space space); + //======================================================== + void storeUserSpaceRole(User user, Space space, String roleKey); + UserSpaceRole loadUserSpaceRole(long id); + void removeUserSpaceRole(UserSpaceRole userSpaceRole); + //======================================================== + void storeMetadata(Metadata metadata); + Metadata loadMetadata(long id); + //======================================================== + String generatePassword(); + String encodeClearText(String clearText); + Map<String, String> getLocales(); + String getDefaultLocale(); + String getJtracHome(); + int getAttachmentMaxSizeInMb(); + int getSessionTimeoutInMinutes(); + //======================================================== + Map<String, String> loadAllConfig(); + void storeConfig(Config config); + String loadConfig(String param); + //======================================================== + void rebuildIndexes(BatchInfo batchInfo); + boolean validateTextSearchQuery(String text); + //======================================================== + void executeHourlyTask(); + void executePollingTask(); + //======================================================== + String getReleaseVersion(); + String getReleaseTimestamp(); + //======================================================== + List<StoredSearch> loadAllStoredSearch(); + void storeStoredSearch(StoredSearch storedSearch); + void removeStoredSearch(Long id); +} Modified: trunk/jtrac/src/main/java/info/jtrac/JtracDao.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/JtracDao.java 2021-10-27 08:20:04 UTC (rev 1403) +++ trunk/jtrac/src/main/java/info/jtrac/JtracDao.java 2021-10-27 15:30:46 UTC (rev 1404) @@ -30,6 +30,7 @@ import info.jtrac.domain.ItemItem; import info.jtrac.domain.ItemUser; import info.jtrac.domain.SpaceSequence; +import info.jtrac.domain.StoredSearch; import info.jtrac.domain.UserSpaceRole; import java.util.Collection; @@ -107,5 +108,9 @@ List<Config> findAllConfig(); void storeConfig(Config config); Config loadConfig(String key); - + //=========================================== + List<StoredSearch> findAllStoredSearch(); + void storeStoredSearch(StoredSearch storedSearch); + StoredSearch loadStoredSearch(Long id); + void removeStoredSearch(StoredSearch storedSearchToDel); } Modified: trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java 2021-10-27 08:20:04 UTC (rev 1403) +++ trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java 2021-10-27 15:30:46 UTC (rev 1404) @@ -1,844 +1,866 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 - * - * 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 info.jtrac; - -import info.jtrac.domain.Attachment; -import info.jtrac.domain.BatchInfo; -import info.jtrac.domain.Config; -import info.jtrac.domain.Counts; -import info.jtrac.domain.CountsHolder; -import info.jtrac.domain.Field; -import info.jtrac.domain.History; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemItem; -import info.jtrac.domain.ItemRefId; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.ItemUser; -import info.jtrac.domain.Metadata; -import info.jtrac.domain.Role; -import info.jtrac.domain.Space; -import info.jtrac.domain.SpaceSequence; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; -import info.jtrac.lucene.IndexSearcher; -import info.jtrac.lucene.Indexer; -import info.jtrac.mail.MailSender; -import info.jtrac.util.AttachmentUtils; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; - -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.Set; - - -import org.acegisecurity.providers.encoding.PasswordEncoder; -import org.acegisecurity.userdetails.UserDetails; -import org.acegisecurity.userdetails.UsernameNotFoundException; -import org.springframework.context.MessageSource; -import org.springframework.util.StringUtils; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Jtrac Service Layer implementation - * This is where all the business logic is - * For data persistence this delegates to JtracDao - */ -public class JtracImpl implements Jtrac { - - private static final Logger logger = LoggerFactory.getLogger(JtracImpl.class); - - private JtracDao dao; - private PasswordEncoder passwordEncoder; - private MailSender mailSender; - private Indexer indexer; - private IndexSearcher indexSearcher; - private MessageSource messageSource; - - private Map<String, String> locales; - private String defaultLocale = "en"; - private String releaseVersion; - private String releaseTimestamp; - private String jtracHome; - private int attachmentMaxSizeInMb = 5; - private int sessionTimeoutInMinutes = 30; - - public void setLocaleList(String[] array) { - locales = new LinkedHashMap<String, String>(); - for(String localeString : array) { - Locale locale = StringUtils.parseLocaleString(localeString); - locales.put(localeString, localeString + " - " + locale.getDisplayName()); - } - logger.info("available locales configured " + locales); - } - - public void setDao(JtracDao dao) { - this.dao = dao; - } - - public void setPasswordEncoder(PasswordEncoder passwordEncoder) { - this.passwordEncoder = passwordEncoder; - } - - public void setIndexSearcher(IndexSearcher indexSearcher) { - this.indexSearcher = indexSearcher; - } - - public void setIndexer(Indexer indexer) { - this.indexer = indexer; - } - - public void setMessageSource(MessageSource messageSource) { - this.messageSource = messageSource; - } - - public void setReleaseTimestamp(String releaseTimestamp) { - this.releaseTimestamp = releaseTimestamp; - } - - public void setReleaseVersion(String releaseVersion) { - this.releaseVersion = releaseVersion; - } - - public void setJtracHome(String jtracHome) { - this.jtracHome = jtracHome; - } - - public String getJtracHome() { - return jtracHome; - } - - public int getAttachmentMaxSizeInMb() { - return attachmentMaxSizeInMb; - } - - public int getSessionTimeoutInMinutes() { - return sessionTimeoutInMinutes; - } - - /** - * this has not been factored into the util package or a helper class - * because it depends on the PasswordEncoder configured - */ - public String generatePassword() { - byte[] ab = new byte[1]; - Random r = new Random(); - r.nextBytes(ab); - return passwordEncoder.encodePassword(new String(ab), null).substring(24); - } - - /** - * this has not been factored into the util package or a helper class - * because it depends on the PasswordEncoder configured - */ - public String encodeClearText(String clearText) { - return passwordEncoder.encodePassword(clearText, null); - } - - public Map<String, String> getLocales() { - return locales; - } - - public String getDefaultLocale() { - return defaultLocale; - } - - /** - * this is automatically called by spring init-method hook on - * startup, also called whenever config is edited to refresh - * TODO move config into a settings class to reduce service clutter - */ - public void init() { - Map<String, String> config = loadAllConfig(); - initDefaultLocale(config.get("locale.default")); - initMailSender(config); - initAttachmentMaxSize(config.get("attachment.maxsize")); - initSessionTimeout(config.get("session.timeout")); - } - - private void initMailSender(Map<String, String> config) { - this.mailSender = new MailSender(config, messageSource, defaultLocale); - } - - private void initDefaultLocale(String localeString) { - if (localeString == null || !locales.containsKey(localeString)) { - logger.warn("invalid default locale configured = '" + localeString + "', using " + this.defaultLocale); - } else { - this.defaultLocale = localeString; - } - logger.info("default locale set to '" + this.defaultLocale + "'"); - } - - private void initAttachmentMaxSize(String s) { - try { - this.attachmentMaxSizeInMb = Integer.parseInt(s); - } catch(Exception e) { - logger.warn("invalid attachment max size '" + s + "', using " + attachmentMaxSizeInMb); - } - logger.info("attachment max size set to " + this.attachmentMaxSizeInMb + " MB"); - } - - private void initSessionTimeout(String s) { - try { - this.sessionTimeoutInMinutes = Integer.parseInt(s); - } catch(Exception e) { - logger.warn("invalid session timeout '" + s + "', using " + this.sessionTimeoutInMinutes); - } - logger.info("session timeout set to " + this.sessionTimeoutInMinutes + " minutes"); - } - - //========================================================================== - - private Attachment getAttachment(FileUpload fileUpload) { - if(fileUpload == null) { - return null; - } - logger.debug("fileUpload not null"); - String fileName = AttachmentUtils.cleanFileName(fileUpload.getClientFileName()); - Attachment attachment = new Attachment(); - attachment.setFileName(fileName); - dao.storeAttachment(attachment); - attachment.setFilePrefix(attachment.getId()); - return attachment; - } - - private void writeToFile(FileUpload fileUpload, Attachment attachment) { - if(fileUpload == null) { - return; - } - File file = AttachmentUtils.getFile(attachment, jtracHome); - try { - fileUpload.writeTo(file); - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - - public synchronized void storeItem(Item item, FileUpload fileUpload) { - History history = new History(item); - Attachment attachment = getAttachment(fileUpload); - if(attachment != null) { - item.add(attachment); - history.setAttachment(attachment); - } - // timestamp can be set by import, then retain - Date now = item.getTimeStamp(); - if(now == null) { - now = new Date(); - } - item.setTimeStamp(now); - history.setTimeStamp(now); - item.add(history); - item.setSequenceNum(dao.loadNextSequenceNum(item.getSpace().getId())); - // this will at the moment execute unnecessary updates (bug in Hibernate handling of "version" property) - // se http://opensource.atlassian.com/projects/hibernate/browse/HHH-1401 - // TODO confirm if above does not happen anymore - dao.storeItem(item); - writeToFile(fileUpload, attachment); - if(indexer != null) { - indexer.index(item); - indexer.index(history); - } - if (item.isSendNotifications()) { - mailSender.send(item); - } - } - - public synchronized void storeItems(List<Item> items) { - for(Item item : items) { - item.setSendNotifications(false); - if(item.getStatus() == State.CLOSED) { - // we support CLOSED items for import also but for consistency - // simulate the item first created OPEN and then being CLOSED - item.setStatus(State.OPEN); - History history = new History(); - history.setTimeStamp(item.getTimeStamp()); - // need to do this as storeHistoryForItem does some role checks - // and so to avoid lazy initialization exception - history.setLoggedBy(loadUser(item.getLoggedBy().getId())); - history.setAssignedTo(item.getAssignedTo()); - history.setComment("-"); - history.setStatus(State.CLOSED); - history.setSendNotifications(false); - storeItem(item, null); - storeHistoryForItem(item.getId(), history, null); - } else { - storeItem(item, null); - } - } - } - - public synchronized void updateItem(Item item, User user) { - logger.debug("update item called"); - History history = new History(item); - history.setAssignedTo(null); - history.setStatus(null); - history.setLoggedBy(user); - history.setComment(item.getEditReason()); - history.setTimeStamp(new Date()); - item.add(history); - dao.storeItem(item); // merge edits + history - // TODO index? - if (item.isSendNotifications()) { - mailSender.send(item); - } - } - - public synchronized void storeHistoryForItem(long itemId, History history, FileUpload fileUpload) { - Item item = dao.loadItem(itemId); - // first apply edits onto item record before we change the item status - // the item.getEditableFieldList routine depends on the current State of the item - for(Field field : item.getEditableFieldList(history.getLoggedBy())) { - Object value = history.getValue(field.getName()); - if (value != null) { - item.setValue(field.getName(), value); - } - } - if (history.getStatus() != null) { - item.setStatus(history.getStatus()); - item.setAssignedTo(history.getAssignedTo()); // this may be null, when closing - } - item.setItemUsers(history.getItemUsers()); - // may have been set if this is an import - if(history.getTimeStamp() == null) { - history.setTimeStamp(new Date()); - } - Attachment attachment = getAttachment(fileUpload); - if(attachment != null) { - item.add(attachment); - history.setAttachment(attachment); - } - item.add(history); - dao.storeItem(item); - writeToFile(fileUpload, attachment); - if(indexer != null) { - indexer.index(history); - } - if (history.isSendNotifications()) { - mailSender.send(item); - } - } - - public Item loadItem(long id) { - return dao.loadItem(id); - } - - public Item loadItemByRefId(String refId) { - ItemRefId itemRefId = new ItemRefId(refId); // throws runtime exception if invalid id - List<Item> items = dao.findItems(itemRefId.getSequenceNum(), itemRefId.getPrefixCode()); - if (items.size() == 0) { - return null; - } - return items.get(0); - } - - public History loadHistory(long id) { - return dao.loadHistory(id); - } - - public List<Item> findItems(ItemSearch itemSearch) { - String searchText = itemSearch.getSearchText(); - if (searchText != null) { - List<Long> hits = indexSearcher.findItemIdsContainingText(searchText); - if (hits.size() == 0) { - itemSearch.setResultCount(0); - return Collections.<Item>emptyList(); - } - itemSearch.setItemIds(hits); - } - return dao.findItems(itemSearch); - } - - public int loadCountOfAllItems() { - return dao.loadCountOfAllItems(); - } - - public List<Item> findAllItems(int firstResult, int batchSize) { - return dao.findAllItems(firstResult, batchSize); - } - - public void removeItem(Item item) { - if(item.getRelatingItems() != null) { - for(ItemItem itemItem : item.getRelatingItems()) { - removeItemItem(itemItem); - } - } - if(item.getRelatedItems() != null) { - for(ItemItem itemItem : item.getRelatedItems()) { - removeItemItem(itemItem); - } - } - dao.removeItem(item); - } - - public void removeItemItem(ItemItem itemItem) { - dao.removeItemItem(itemItem); - } - - public int loadCountOfRecordsHavingFieldNotNull(Space space, Field field) { - return dao.loadCountOfRecordsHavingFieldNotNull(space, field); - } - - public int bulkUpdateFieldToNull(Space space, Field field) { - return dao.bulkUpdateFieldToNull(space, field); - } - - public int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey) { - return dao.loadCountOfRecordsHavingFieldWithValue(space, field, optionKey); - } - - public int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey) { - return dao.bulkUpdateFieldToNullForValue(space, field, optionKey); - } - - public int loadCountOfRecordsHavingStatus(Space space, int status) { - return dao.loadCountOfRecordsHavingStatus(space, status); - } - - public int bulkUpdateStatusToOpen(Space space, int status) { - return dao.bulkUpdateStatusToOpen(space, status); - } - - public int bulkUpdateRenameSpaceRole(Space space, String oldRoleKey, String newRoleKey) { - return dao.bulkUpdateRenameSpaceRole(space, oldRoleKey, newRoleKey); - } - - public int bulkUpdateDeleteSpaceRole(Space space, String roleKey) { - return dao.bulkUpdateDeleteSpaceRole(space, roleKey); - } - - // ========= Acegi UserDetailsService implementation ========== - public UserDetails loadUserByUsername(String loginName) { - List<User> users = null; - if (loginName.indexOf("@") != -1) { - users = dao.findUsersByEmail(loginName); - } else { - users = dao.findUsersByLoginName(loginName); - } - if (users.size() == 0) { - throw new UsernameNotFoundException("User not found for '" + loginName + "'"); - } - logger.debug("loadUserByUserName success for '" + loginName + "'"); - User user = users.get(0); - // if some spaces have guest access enabled, allocate these spaces as well - Set<Space> userSpaces = user.getSpaces(); - logger.debug("user spaces: " + userSpaces); - for(Space s : findSpacesWhereGuestAllowed()) { - if(!userSpaces.contains(s)) { - user.addSpaceWithRole(s, Role.ROLE_GUEST); - - } - } - for(UserSpaceRole usr : user.getSpaceRoles()) { - logger.debug("UserSpaceRole: " + usr); - // this is a hack, the effect of the next line would be to - // override hibernate lazy loading and get the space and associated metadata. - // since this only happens only once on authentication and simplifies a lot of - // code later because the security principal is "fully prepared", - // this is hopefully pardonable. The downside is that there may be as many extra db hits - // as there are spaces allocated for the user. Hibernate caching should alleviate this - usr.isAbleToCreateNewItem(); - } - return user; - } - - public User loadUser(long id) { - return dao.loadUser(id); - } - - public User loadUser(String loginName) { - List<User> users = dao.findUsersByLoginName(loginName); - if (users.size() == 0) { - return null; - } - return users.get(0); - } - - public void storeUser(User user) { - user.clearNonPersistentRoles(); - dao.storeUser(user); - } - - public void storeUser(User user, String password, boolean sendNotifications) { - if (password == null) { - password = generatePassword(); - } - user.setPassword(encodeClearText(password)); - storeUser(user); - if(sendNotifications) { - mailSender.sendUserPassword(user, password); - } - } - - public void removeUser(User user) { - for(ItemUser iu : dao.findItemUsersByUser(user)) { - dao.removeItemUser(iu); - } - dao.removeUser(user); - } - - public List<User> findAllUsers() { - return dao.findAllUsers(); - } - - public List<User> findUsersWhereIdIn(List<Long> ids) { - return dao.findUsersWhereIdIn(ids); - } - - public List<User> findUsersMatching(String searchText, String searchOn) { - return dao.findUsersMatching(searchText, searchOn); - } - - public List<User> findUsersForSpace(long spaceId) { - return dao.findUsersForSpace(spaceId); - } - - public List<UserSpaceRole> findUserRolesForSpace(long spaceId) { - return dao.findUserRolesForSpace(spaceId); - } - - public Map<Long, List<UserSpaceRole>> loadUserRolesMapForSpace(long spaceId) { - List<UserSpaceRole> list = dao.findUserRolesForSpace(spaceId); - Map<Long, List<UserSpaceRole>> map = new LinkedHashMap<Long, List<UserSpaceRole>>(); - for(UserSpaceRole usr : list) { - long userId = usr.getUser().getId(); - List<UserSpaceRole> value = map.get(userId); - if(value == null) { - value = new ArrayList<UserSpaceRole>(); - map.put(userId, value); - } - value.add(usr); - } - return map; - } - - public Map<Long, List<UserSpaceRole>> loadSpaceRolesMapForUser(long userId) { - List<UserSpaceRole> list = dao.findSpaceRolesForUser(userId); - Map<Long, List<UserSpaceRole>> map = new LinkedHashMap<Long, List<UserSpaceRole>>(); - for(UserSpaceRole usr : list) { - long spaceId = usr.getSpace() == null ? 0 : usr.getSpace().getId(); - List<UserSpaceRole> value = map.get(spaceId); - if(value == null) { - value = new ArrayList<UserSpaceRole>(); - map.put(spaceId, value); - } - value.add(usr); - } - return map; - } - - public List<User> findUsersWithRoleForSpace(long spaceId, String roleKey) { - return dao.findUsersWithRoleForSpace(spaceId, roleKey); - } - - public List<User> findUsersForUser(User user) { - Set<Space> spaces = user.getSpaces(); - if(spaces.size() == 0) { - // this will happen when a user has no spaces allocated - return Collections.emptyList(); - } - // must be a better way to make this unique? - List<User> users = dao.findUsersForSpaceSet(spaces); - Set<User> userSet = new LinkedHashSet<User>(users); - return new ArrayList<User>(userSet); - } - - public List<User> findUsersNotFullyAllocatedToSpace(long spaceId) { - // trying to reduce database hits and lazy loading as far as possible - List<User> notAtAllAllocated = dao.findUsersNotAllocatedToSpace(spaceId); - List<UserSpaceRole> usrs = dao.findUserRolesForSpace(spaceId); - List<User> notFullyAllocated = new ArrayList<User>(notAtAllAllocated); - if(usrs.size() == 0) { - return notFullyAllocated; - } - Space space = usrs.get(0).getSpace(); - Set<UserSpaceRole> allocated = new HashSet(usrs); - Set<String> roleKeys = new HashSet(space.getMetadata().getAllRoleKeys()); - Set<User> processed = new HashSet<User>(usrs.size()); - Set<User> superUsers = new HashSet(dao.findSuperUsers()); - for(UserSpaceRole usr : usrs) { - User user = usr.getUser(); - if(processed.contains(user)) { - continue; - } - processed.add(user); - // not using the user object as it is db intensive - boolean isSuperUser = superUsers.contains(user); - for(String roleKey : roleKeys) { - if(isSuperUser && Role.isAdmin(roleKey)) { - continue; - } - UserSpaceRole temp = new UserSpaceRole(user, space, roleKey); - if(!allocated.contains(temp)) { - notFullyAllocated.add(user); - break; - } - } - } - Collections.sort(notFullyAllocated); - return notFullyAllocated; - } - - public int loadCountOfHistoryInvolvingUser(User user) { - return dao.loadCountOfHistoryInvolvingUser(user); - } - - //========================================================================== - - public CountsHolder loadCountsForUser(User user) { - return dao.loadCountsForUser(user); - } - - public Counts loadCountsForUserSpace(User user, Space space) { - return dao.loadCountsForUserSpace(user, space); - } - - //========================================================================== - - public void storeUserSpaceRole(User user, Space space, String roleKey) { - user.addSpaceWithRole(space, roleKey); - storeUser(user); - } - - public void removeUserSpaceRole(UserSpaceRole userSpaceRole) { - User user = userSpaceRole.getUser(); - user.removeSpaceWithRole(userSpaceRole.getSpace(), userSpaceRole.getRoleKey()); - // dao.storeUser(user); - dao.removeUserSpaceRole(userSpaceRole); - } - - public UserSpaceRole loadUserSpaceRole(long id) { - return dao.loadUserSpaceRole(id); - } - - //========================================================================== - - public Space loadSpace(long id) { - return dao.loadSpace(id); - } - - public Space loadSpace(String prefixCode) { - List<Space> spaces = dao.findSpacesByPrefixCode(prefixCode); - if (spaces.size() == 0) { - return null; - } - return spaces.get(0); - } - - public void storeSpace(Space space) { - boolean newSpace = space.getId() == 0; - dao.storeSpace(space); - if(newSpace) { - SpaceSequence ss = new SpaceSequence(); - ss.setNextSeqNum(1); - ss.setId(space.getId()); - dao.storeSpaceSequence(ss); - } - } - - public List<Space> findAllSpaces() { - return dao.findAllSpaces(); - } - - public List<Space> findSpacesWhereIdIn(List<Long> ids) { - return dao.findSpacesWhereIdIn(ids); - } - - public List<Space> findSpacesWhereGuestAllowed() { - return dao.findSpacesWhereGuestAllowed(); - } - - public List<Space> findSpacesNotFullyAllocatedToUser(long userId) { - // trying to reduce database hits and lazy loading as far as possible - List<Space> notAtAllAllocated = dao.findSpacesNotAllocatedToUser(userId); - List<UserSpaceRole> usrs = dao.findSpaceRolesForUser(userId); - List<Space> notFullyAllocated = new ArrayList(notAtAllAllocated); - if(usrs.size() == 0) { - return notFullyAllocated; - } - Set<UserSpaceRole> allocated = new HashSet(usrs); - Set<Space> processed = new HashSet<Space>(usrs.size()); - User user = usrs.get(0).getUser(); - boolean isSuperUser = user.isSuperUser(); - for(UserSpaceRole usr : usrs) { - Space space = usr.getSpace(); - if(space == null || processed.contains(space)) { - continue; - } - processed.add(space); - for(String roleKey : space.getMetadata().getAllRoleKeys()) { - if(isSuperUser && Role.isAdmin(roleKey)) { - continue; - } - UserSpaceRole temp = new UserSpaceRole(user, space, roleKey); - if(!allocated.contains(temp)) { - notFullyAllocated.add(space); - break; - } - } - } - Collections.sort(notFullyAllocated); - return notFullyAllocated; - } - - public void removeSpace(Space space) { - logger.info("proceeding to delete space: " + space); - dao.bulkUpdateDeleteSpaceRole(space, null); - dao.bulkUpdateDeleteItemsForSpace(space); - dao.removeSpace(space); - logger.info("successfully deleted space"); - } - - //========================================================================== - - public void storeMetadata(Metadata metadata) { - dao.storeMetadata(metadata); - } - - public Metadata loadMetadata(long id) { - return dao.loadMetadata(id); - } - - //========================================================================== - - public Map<String, String> loadAllConfig() { - List<Config> list = dao.findAllConfig(); - Map<String, String> allConfig = new HashMap<String, String>(list.size()); - for (Config c : list) { - allConfig.put(c.getParam(), c.getValue()); - } - return allConfig; - } - - // TODO must be some nice generic way to do this - public void storeConfig(Config config) { - dao.storeConfig(config); - if(config.isMailConfig()) { - initMailSender(loadAllConfig()); - } else if(config.isLocaleConfig()) { - initDefaultLocale(config.getValue()); - } else if(config.isAttachmentConfig()) { - initAttachmentMaxSize(config.getValue()); - } else if(config.isSessionTimeoutConfig()) { - initSessionTimeout(config.getValue()); - } - } - - public String loadConfig(String param) { - Config config = dao.loadConfig(param); - if (config == null) { - return null; - } - String value = config.getValue(); - if (value == null || value.trim().equals("")) { - return null; - } - return value; - } - - //======================================================== - - public void rebuildIndexes(BatchInfo batchInfo) { - File file = new File(jtracHome + "/indexes"); - for (File f : file.listFiles()) { - logger.debug("deleting file: " + f); - f.delete(); - } - logger.info("existing index files deleted successfully"); - int totalSize = dao.loadCountOfAllItems(); - batchInfo.setTotalSize(totalSize); - logger.info("total items to index: " + totalSize); - int firstResult = 0; - long lastFetchedId = 0; - while(true) { - logger.info("processing batch starting from: " + firstResult + ", current: " + batchInfo.getCurrentPosition()); - List<Item> items = dao.findAllItems(firstResult, batchInfo.getBatchSize()); - for (Item item : items) { - - indexer.index(item); - - // currently history is indexed separately from item - // not sure if this is a good thing, maybe it gives - // more flexibility e.g. fine-grained search results - - int historyCount = 0; - for(History history : item.getHistory()) { - indexer.index(history); - historyCount++; - } - if(logger.isDebugEnabled()) { - logger.debug("indexed item: " + item.getId() - + " : " + item.getRefId() + ", history: " + historyCount); - } - batchInfo.incrementPosition(); - lastFetchedId = item.getId(); - } - if(logger.isDebugEnabled()) { - logger.debug("size of current batch: " + items.size()); - logger.debug("last fetched Id: " + lastFetchedId); - } - firstResult += batchInfo.getBatchSize(); - if(logger.isDebugEnabled()) { - logger.debug("setting firstResult to: " + firstResult); - } - if(batchInfo.isComplete()) { - logger.info("batch completed at position: " + batchInfo.getCurrentPosition()); - break; - } - } - - } - - public boolean validateTextSearchQuery(String text) { - return indexSearcher.validateQuery(text); - } - - //========================================================================== - - public void executeHourlyTask() { - logger.debug("hourly task called"); - } - - /* configured to be called every five minutes */ - public void executePollingTask() { - logger.debug("polling task called"); - } - - //========================================================================== - - public String getReleaseVersion() { - return releaseVersion; - } - - public String getReleaseTimestamp() { - return releaseTimestamp; - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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 info.jtrac; + +import info.jtrac.domain.Attachment; +import info.jtrac.domain.BatchInfo; +import info.jtrac.domain.Config; +import info.jtrac.domain.Counts; +import info.jtrac.domain.CountsHolder; +import info.jtrac.domain.Field; +import info.jtrac.domain.History; +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemItem; +import info.jtrac.domain.ItemRefId; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.ItemUser; +import info.jtrac.domain.Metadata; +import info.jtrac.domain.Role; +import info.jtrac.domain.Space; +import info.jtrac.domain.SpaceSequence; +import info.jtrac.domain.State; +import info.jtrac.domain.StoredSearch; +import info.jtrac.domain.User; +import info.jtrac.domain.UserSpaceRole; +import info.jtrac.lucene.IndexSearcher; +import info.jtrac.lucene.Indexer; +import info.jtrac.mail.MailSender; +import info.jtrac.util.AttachmentUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.acegisecurity.providers.encoding.PasswordEncoder; +import org.acegisecurity.userdetails.UserDetails; +import org.acegisecurity.userdetails.UsernameNotFoundException; + +import org.springframework.context.MessageSource; +import org.springframework.util.StringUtils; + +import org.apache.wicket.markup.html.form.upload.FileUpload; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Jtrac Service Layer implementation + * This is where all the business logic is + * For data persistence this delegates to JtracDao + */ +public class JtracImpl implements Jtrac { + + private static final Logger logger = LoggerFactory.getLogger(JtracImpl.class); + + private JtracDao dao; + private PasswordEncoder passwordEncoder; + private MailSender mailSender; + private Indexer indexer; + private IndexSearcher indexSearcher; + private MessageSource messageSource; + + private Map<String, String> locales; + private String defaultLocale = "en"; + private String releaseVersion; + private String releaseTimestamp; + private String jtracHome; + private int attachmentMaxSizeInMb = 5; + private int sessionTimeoutInMinutes = 30; + + public void setLocaleList(String[] array) { + locales = new LinkedHashMap<String, String>(); + for(String localeString : array) { + Locale locale = StringUtils.parseLocaleString(localeString); + locales.put(localeString, localeString + " - " + locale.getDisplayName()); + } + logger.info("available locales configured " + locales); + } + + public void setDao(JtracDao dao) { + this.dao = dao; + } + + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + public void setIndexSearcher(IndexSearcher indexSearcher) { + this.indexSearcher = indexSearcher; + } + + public void setIndexer(Indexer indexer) { + this.indexer = indexer; + } + + public void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + public void setReleaseTimestamp(String releaseTimestamp) { + this.releaseTimestamp = releaseTimestamp; + } + + public void setReleaseVersion(String releaseVersion) { + this.releaseVersion = releaseVersion; + } + + public void setJtracHome(String jtracHome) { + this.jtracHome = jtracHome; + } + + public String getJtracHome() { + return jtracHome; + } + + public int getAttachmentMaxSizeInMb() { + return attachmentMaxSizeInMb; + } + + public int getSessionTimeoutInMinutes() { + return sessionTimeoutInMinutes; + } + + /** + * this has not been factored into the util package or a helper class + * because it depends on the PasswordEncoder configured + */ + public String generatePassword() { + byte[] ab = new byte[1]; + Random r = new Random(); + r.nextBytes(ab); + return passwordEncoder.encodePassword(new String(ab), null).substring(24); + } + + /** + * this has not been factored into the util package or a helper class + * because it depends on the PasswordEncoder configured + */ + public String encodeClearText(String clearText) { + return passwordEncoder.encodePassword(clearText, null); + } + + public Map<String, String> getLocales() { + return locales; + } + + public String getDefaultLocale() { + return defaultLocale; + } + + /** + * this is automatically called by spring init-method hook on + * startup, also called whenever config is edited to refresh + * TODO move config into a settings class to reduce service clutter + */ + public void init() { + Map<String, String> config = loadAllConfig(); + initDefaultLocale(config.get("locale.default")); + initMailSender(config); + initAttachmentMaxSize(config.get("attachment.maxsize")); + initSessionTimeout(config.get("session.timeout")); + } + + private void initMailSender(Map<String, String> config) { + this.mailSender = new MailSender(config, messageSource, defaultLocale); + } + + private void initDefaultLocale(String localeString) { + if (localeString == null || !locales.containsKey(localeString)) { + logger.warn("invalid default locale configured = '" + localeString + "', using " + this.defaultLocale); + } else { + this.defaultLocale = localeString; + } + logger.info("default locale set to '" + this.defaultLocale + "'"); + } + + private void initAttachmentMaxSize(String s) { + try { + this.attachmentMaxSizeInMb = Integer.parseInt(s); + } catch(Exception e) { + logger.warn("invalid attachment max size '" + s + "', using " + attachmentMaxSizeInMb); + } + logger.info("attachment max size set to " + this.attachmentMaxSizeInMb + " MB"); + } + + private void initSessionTimeout(String s) { + try { + this.sessionTimeoutInMinutes = Integer.parseInt(s); + } catch(Exception e) { + logger.warn("invalid session timeout '" + s + "', using " + this.sessionTimeoutInMinutes); + } + logger.info("session timeout set to " + this.sessionTimeoutInMinutes + " minutes"); + } + + //========================================================================== + + private Attachment getAttachment(FileUpload fileUpload) { + if(fileUpload == null) { + return null; + } + logger.debug("fileUpload not null"); + String fileName = AttachmentUtils.cleanFileName(fileUpload.getClientFileName()); + Attachment attachment = new Attachment(); + attachment.setFileName(fileName); + dao.storeAttachment(attachment); + attachment.setFilePrefix(attachment.getId()); + return attachment; + } + + private void writeToFile(FileUpload fileUpload, Attachment attachment) { + if(fileUpload == null) { + return; + } + File file = AttachmentUtils.getFile(attachment, jtracHome); + try { + fileUpload.writeTo(file); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + public synchronized void storeItem(Item item, FileUpload fileUpload) { + History history = new History(item); + Attachment attachment = getAttachment(fileUpload); + if(attachment != null) { + item.add(attachment); + history.setAttachment(attachment); + } + // timestamp can be set by import, then retain + Date now = item.getTimeStamp(); + if(now == null) { + now = new Date(); + } + item.setTimeStamp(now); + history.setTimeStamp(now); + item.add(history); + item.setSequenceNum(dao.loadNextSequenceNum(item.getSpace().getId())); + // this will at the moment execute unnecessary updates (bug in Hibernate handling of "version" property) + // se http://opensource.atlassian.com/projects/hibernate/browse/HHH-1401 + // TODO confirm if above does not happen anymore + dao.storeItem(item); + writeToFile(fileUpload, attachment); + if(indexer != null) { + indexer.index(item); + indexer.index(history); + } + if (item.isSendNotifications()) { + mailSender.send(item); + } + } + + public synchronized void storeItems(List<Item> items) { + for(Item item : items) { + item.setSendNotifications(false); + if(item.getStatus() == State.CLOSED) { + // we support CLOSED items for import also but for consistency + // simulate the item first created OPEN and then being CLOSED + item.setStatus(State.OPEN); + History history = new History(); + history.setTimeStamp(item.getTimeStamp()); + // need to do this as storeHistoryForItem does some role checks + // and so to avoid lazy initialization exception + history.setLoggedBy(loadUser(item.getLoggedBy().getId())); + history.setAssignedTo(item.getAssignedTo()); + history.setComment("-"); + history.setStatus(State.CLOSED); + history.setSendNotifications(false); + storeItem(item, null); + storeHistoryForItem(item.getId(), history, null); + } else { + storeItem(item, null); + } + } + } + + public synchronized void updateItem(Item item, User user) { + logger.debug("update item called"); + History history = new History(item); + history.setAssignedTo(null); + history.setStatus(null); + history.setLoggedBy(user); + history.setComment(item.getEditReason()); + history.setTimeStamp(new Date()); + item.add(history); + dao.storeItem(item); // merge edits + history + // TODO index? + if (item.isSendNotifications()) { + mailSender.send(item); + } + } + + public synchronized void storeHistoryForItem(long itemId, History history, FileUpload fileUpload) { + Item item = dao.loadItem(itemId); + // first apply edits onto item record before we change the item status + // the item.getEditableFieldList routine depends on the current State of the item + for(Field field : item.getEditableFieldList(history.getLoggedBy())) { + Object value = history.getValue(field.getName()); + if (value != null) { + item.setValue(field.getName(), value); + } + } + if (history.getStatus() != null) { + item.setStatus(history.getStatus()); + item.setAssignedTo(history.getAssignedTo()); // this may be null, when closing + } + item.setItemUsers(history.getItemUsers()); + // may have been set if this is an import + if(history.getTimeStamp() == null) { + history.setTimeStamp(new Date()); + } + Attachment attachment = getAttachment(fileUpload); + if(attachment != null) { + item.add(attachment); + history.setAttachment(attachment); + } + item.add(history); + dao.storeItem(item); + writeToFile(fileUpload, attachment); + if(indexer != null) { + indexer.index(history); + } + if (history.isSendNotifications()) { + mailSender.send(item); + } + } + + public Item loadItem(long id) { + return dao.loadItem(id); + } + + public Item loadItemByRefId(String refId) { + ItemRefId itemRefId = new ItemRefId(refId); // throws runtime exception if invalid id + List<Item> items = dao.findItems(itemRefId.getSequenceNum(), itemRefId.getPrefixCode()); + if (items.size() == 0) { + return null; + } + return items.get(0); + } + + public History loadHistory(long id) { + return dao.loadHistory(id); + } + + public List<Item> findItems(ItemSearch itemSearch) { + String searchText = itemSearch.getSearchText(); + if (searchText != null) { + List<Long> hits = indexSearcher.findItemIdsContainingText(searchText); + if (hits.size() == 0) { + itemSearch.setResultCount(0); + return Collections.<Item>emptyList(); + } + itemSearch.setItemIds(hits); + } + return dao.findItems(itemSearch); + } + + public int loadCountOfAllItems() { + return dao.loadCountOfAllItems(); + } + + public List<Item> findAllItems(int firstResult, int batchSize) { + return dao.findAllItems(firstResult, batchSize); + } + + public void removeItem(Item item) { + if(item.getRelatingItems() != null) { + for(ItemItem itemItem : item.getRelatingItems()) { + removeItemItem(itemItem); + } + } + if(item.getRelatedItems() != null) { + for(ItemItem itemItem : item.getRelatedItems()) { + removeItemItem(itemItem); + } + } + dao.removeItem(item); + } + + public void removeItemItem(ItemItem itemItem) { + dao.removeItemItem(itemItem); + } + + public int loadCountOfRecordsHavingFieldNotNull(Space space, Field field) { + return dao.loadCountOfRecordsHavingFieldNotNull(space, field); + } + + public int bulkUpdateFieldToNull(Space space, Field field) { + return dao.bulkUpdateFieldToNull(space, field); + } + + public int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey) { + return dao.loadCountOfRecordsHavingFieldWithValue(space, field, optionKey); + } + + public int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey) { + return dao.bulkUpdateFieldToNullForValue(space, field, optionKey); + } + + public int loadCountOfRecordsHavingStatus(Space space, int status) { + return dao.loadCountOfRecordsHavingStatus(space, status); + } + + public int bulkUpdateStatusToOpen(Space space, int status) { + return dao.bulkUp... [truncated message content] |