///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.plugins.todo; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.sql.DataSource; import org.apache.commons.lang.ObjectUtils; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.projectforge.continuousdb.Table; import org.projectforge.core.BaseDao; import org.projectforge.core.BaseSearchFilter; import org.projectforge.core.ConfigXml; import org.projectforge.core.DisplayHistoryEntry; import org.projectforge.core.ModificationStatus; import org.projectforge.core.QueryFilter; import org.projectforge.mail.Mail; import org.projectforge.mail.SendMail; import org.projectforge.task.TaskDO; import org.projectforge.task.TaskNode; import org.projectforge.task.TaskTree; import org.projectforge.user.GroupDO; import org.projectforge.user.GroupDao; import org.projectforge.user.I18nHelper; import org.projectforge.user.PFUserContext; import org.projectforge.user.PFUserDO; import org.projectforge.user.UserDao; import org.projectforge.user.UserRightId; import org.springframework.jdbc.core.JdbcTemplate; /** * * @author Kai Reinhard (k.reinhard@micromata.de) * */ public class ToDoDao extends BaseDao<ToDoDO> { public static final UserRightId USER_RIGHT_ID = new UserRightId("PLUGIN_TODO", "plugin10", "plugins.todo.todo");; private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(ToDoDao.class); private static final String[] ADDITIONAL_SEARCH_FIELDS = new String[] { "reporter.username", "reporter.firstname", "reporter.lastname", "assignee.username", "assignee.firstname", "assignee.lastname", "task.title", "task.taskpath", "group.name"}; private final Table table = new Table(ToDoDO.class); private DataSource dataSource; private GroupDao groupDao; private UserDao userDao; private SendMail sendMail; private TaskTree taskTree; private final ToDoCache toDoCache = new ToDoCache(this); public ToDoDao() { super(ToDoDO.class); userRightId = USER_RIGHT_ID; } @Override protected String[] getAdditionalSearchFields() { return ADDITIONAL_SEARCH_FIELDS; } @Override public List<ToDoDO> getList(final BaseSearchFilter filter) { final ToDoFilter myFilter; if (filter instanceof ToDoFilter) { myFilter = (ToDoFilter) filter; } else { myFilter = new ToDoFilter(filter); } final QueryFilter queryFilter = new QueryFilter(myFilter); final Collection<ToDoStatus> col = new ArrayList<ToDoStatus>(5); final String searchString = myFilter.getSearchString(); if (myFilter.isOnlyRecent() == true) { final PFUserDO assignee = new PFUserDO(); assignee.setId(PFUserContext.getUserId()); queryFilter.add(Restrictions.eq("assignee", assignee)); myFilter.setSearchString(""); // Delete search string for ignoring it. queryFilter.add(Restrictions.eq("recent", true)); } else { if (myFilter.isOpened() == true) { col.add(ToDoStatus.OPENED); } if (myFilter.isClosed() == true) { col.add(ToDoStatus.CLOSED); } if (myFilter.isPostponed() == true) { col.add(ToDoStatus.POSTPONED); } if (myFilter.isReopened() == true) { col.add(ToDoStatus.RE_OPENED); } if (myFilter.isInprogress() == true) { col.add(ToDoStatus.IN_PROGRESS); } if (col.size() > 0) { queryFilter.add(Restrictions.in("status", col)); } if (myFilter.getTaskId() != null) { final TaskNode node = taskTree.getTaskNodeById(myFilter.getTaskId()); final List<Integer> taskIds = node.getDescendantIds(); taskIds.add(node.getId()); queryFilter.add(Restrictions.in("task.id", taskIds)); } if (myFilter.getAssigneeId() != null) { final PFUserDO assignee = new PFUserDO(); assignee.setId(myFilter.getAssigneeId()); queryFilter.add(Restrictions.eq("assignee", assignee)); } if (myFilter.getReporterId() != null) { final PFUserDO reporter = new PFUserDO(); reporter.setId(myFilter.getReporterId()); queryFilter.add(Restrictions.eq("reporter", reporter)); } } queryFilter.addOrder(Order.desc("created")); final List<ToDoDO> list = getList(queryFilter); myFilter.setSearchString(searchString); // Restore search string. return list; } /** * Sends an e-mail to the projekt manager if exists and is not equals to the logged in user. * @param todo * @param operationType * @return */ public void sendNotification(final ToDoDO todo, final String requestUrl) { if (ConfigXml.getInstance().isSendMailConfigured() == false) { // Can't send e-mail because no send mail is configured. return; } final Map<String, Object> data = new HashMap<String, Object>(); data.put("todo", todo); data.put("requestUrl", requestUrl); final List<DisplayHistoryEntry> history = getDisplayHistoryEntries(todo); final List<DisplayHistoryEntry> list = new ArrayList<DisplayHistoryEntry>(); int i = 0; for (final DisplayHistoryEntry entry : history) { list.add(entry); if (++i >= 10) { break; } } data.put("history", list); final PFUserDO user = PFUserContext.getUser(); final Integer userId = user.getId(); final Integer assigneeId = todo.getAssigneeId(); final Integer reporterId = todo.getReporterId(); if (assigneeId != null && userId.equals(assigneeId) == false) { sendNotification(todo.getAssignee(), todo, data, true); } if (reporterId != null && userId.equals(reporterId) == false && reporterId.equals(assigneeId) == false) { sendNotification(todo.getReporter(), todo, data, true); } if (userId != assigneeId && userId != reporterId && hasSelectAccess(user, todo, false) == false) { // User is whether reporter nor assignee, so send e-mail (in the case the user hasn't read access anymore). sendNotification(PFUserContext.getUser(), todo, data, false); } } private void sendNotification(final PFUserDO recipient, final ToDoDO toDo, final Map<String, Object> data, final boolean checkAccess) { if (checkAccess == true && hasSelectAccess(recipient, toDo, false) == false) { log.info("Recipient '" + recipient.getFullname() + "' (id=" + recipient.getId() + ") of the notification has no select access to the todo entry: " + toDo); return; } final Locale locale = recipient.getLocale(); final Mail msg = new Mail(); msg.setTo(recipient); final StringBuffer subject = new StringBuffer(); final ToDoStatus status = toDo.getStatus(); if (status != null && status != ToDoStatus.OPENED) { subject.append("[").append(I18nHelper.getLocalizedString(locale, "plugins.todo.status")).append(": ") .append(I18nHelper.getLocalizedString(locale, status.getI18nKey())).append("] "); } subject.append(I18nHelper.getLocalizedString(locale, "plugins.todo.todo")).append(": "); subject.append(toDo.getSubject()); msg.setProjectForgeSubject(subject.toString()); final String content = sendMail.renderGroovyTemplate(msg, "mail/todoChangeNotification.html", data, recipient); msg.setContent(content); msg.setContentType(Mail.CONTENTTYPE_HTML); sendMail.send(msg, null, null); } @Override protected void onSave(final ToDoDO obj) { if (ObjectUtils.equals(PFUserContext.getUserId(), obj.getAssigneeId()) == false) { // To-do is changed by other user than assignee, so set recent flag for this to-do for the assignee. obj.setRecent(true); } } @Override protected void onChange(final ToDoDO obj, final ToDoDO dbObj) { if (ObjectUtils.equals(PFUserContext.getUserId(), obj.getAssigneeId()) == false) { // To-do is changed by other user than assignee, so set recent flag for this to-do for the assignee. final ToDoDO copyOfDBObj = new ToDoDO(); copyOfDBObj.copyValuesFrom(dbObj, "deleted"); if (copyOfDBObj.copyValuesFrom(obj, "deleted") == ModificationStatus.MAJOR) { // Modifications done: obj.setRecent(true); } } } @Override protected void afterSaveOrModify(final ToDoDO obj) { toDoCache.setExpired(); // Force reload of the menu item counters for open to-do entrie. } public void setAssignee(final ToDoDO todo, final Integer userId) { final PFUserDO user = userDao.getOrLoad(userId); todo.setAssignee(user); } public void setReporter(final ToDoDO todo, final Integer userId) { final PFUserDO user = userDao.getOrLoad(userId); todo.setReporter(user); } public void setSendMail(final SendMail sendMail) { this.sendMail = sendMail; } public void setTask(final ToDoDO todo, final Integer taskId) { final TaskDO task = taskTree.getTaskById(taskId); todo.setTask(task); } public void setGroup(final ToDoDO todo, final Integer groupId) { final GroupDO group = groupDao.getOrLoad(groupId); todo.setGroup(group); } /** * Get the number of open to-do entries for the given user. Entries are open (in this context) when they're not deleted or closed. <br/> * The result is cached (therefore you can call this method very often). * @param userId If null then the current logged in user is assumed. * @return Number of open to-do entries. */ public int getOpenToDoEntries(Integer userId) { if (userId == null) { userId = PFUserContext.getUserId(); } return toDoCache.getOpenToDoEntries(userId); } /** * Called by ToDoCache to get the number of open entries for the given users. * @param userId * @return Number of open to-do entries. */ int internalGetOpenEntries(final Integer userId) { final JdbcTemplate jdbc = new JdbcTemplate(dataSource); try { return jdbc.queryForInt("SELECT COUNT(*) FROM " + table.getName() + " where assignee_fk=" + userId + " and recent=true and deleted=false"); } catch (final Exception ex) { log.error(ex.getMessage(), ex); return 0; } } @Override public ToDoDO newInstance() { return new ToDoDO(); } public void setDataSource(final DataSource dataSource) { this.dataSource = dataSource; } public void setGroupDao(final GroupDao groupDao) { this.groupDao = groupDao; } public void setUserDao(final UserDao userDao) { this.userDao = userDao; } public void setTaskTree(final TaskTree taskTree) { this.taskTree = taskTree; } /** * @see org.projectforge.core.BaseDao#useOwnCriteriaCacheRegion() */ @Override protected boolean useOwnCriteriaCacheRegion() { return true; } }