Package org.projectforge.web.calendar

Source Code of org.projectforge.web.calendar.CalendarFeed

/////////////////////////////////////////////////////////////////////////////
//
// 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.web.calendar;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.ValidationException;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Version;
import net.ftlines.wicket.fullcalendar.Event;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.MDC;
import org.joda.time.DateTime;
import org.projectforge.access.AccessException;
import org.projectforge.calendar.DayHolder;
import org.projectforge.calendar.ICal4JUtils;
import org.projectforge.common.NumberHelper;
import org.projectforge.common.StringHelper;
import org.projectforge.plugins.teamcal.TeamCalConfig;
import org.projectforge.registry.Registry;
import org.projectforge.timesheet.TimesheetDO;
import org.projectforge.timesheet.TimesheetDao;
import org.projectforge.timesheet.TimesheetFilter;
import org.projectforge.user.PFUserContext;
import org.projectforge.user.PFUserDO;
import org.projectforge.user.ProjectForgeGroup;
import org.projectforge.user.UserDao;
import org.projectforge.user.UserRights;
import org.projectforge.web.WebConfiguration;
import org.projectforge.web.timesheet.TimesheetEventsProvider;

/**
* Feed Servlet, which generates a 'text/calendar' output of the last four mounts. Currently relevant informations are date, start- and stop
* time and last but not least the location of an event.
*
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class CalendarFeed extends HttpServlet
{
  private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(CalendarFeed.class);

  private static final long serialVersionUID = 1480433876190009435L;

  private static final int PERIOD_IN_MONTHS = 4;

  private static final String PARAM_NAME_TIMESHEET_USER = "timesheetUser";

  private static final String PARAM_NAME_HOLIDAYS = "holidays";

  private static final String PARAM_NAME_WEEK_OF_YEARS = "weekOfYears";

  private static final List<CalendarFeedHook> feedHooks = new LinkedList<CalendarFeedHook>();

  /**
   * setup event is needed for empty calendars
   */
  public static final String SETUP_EVENT = "SETUP EVENT";

  public static String getUrl()
  {
    return getUrl(null);
  }

  /**
   * @return The url for downloading timesheets (including context), e. g. /ProjectForge/export/ProjectForge.ics?user=....
   */
  public static String getUrl4Timesheets(final Integer timesheetUserId)
  {
    return getUrl("&" + PARAM_NAME_TIMESHEET_USER + "=" + timesheetUserId);
  }

  /**
   * @return The url for downloading timesheets (including context), e. g. /ProjectForge/export/ProjectForge.ics?user=....
   */
  public static String getUrl4Holidays()
  {
    return getUrl("&" + PARAM_NAME_HOLIDAYS + "=true");
  }

  /**
   * @return The url for downloading timesheets (including context), e. g. /ProjectForge/export/ProjectForge.ics?user=....
   */
  public static String getUrl4WeekOfYears()
  {
    return getUrl("&" + PARAM_NAME_WEEK_OF_YEARS + "=true");
  }

  /**
   * @param additionalParams Request parameters such as "&calId=42", may be null.
   * @return The url for downloading calendars (without context), e. g. /export/ProjectForge.ics?user=...
   */
  public static String getUrl(final String additionalParams)
  {
    final PFUserDO user = PFUserContext.getUser();
    final UserDao userDao = Registry.instance().getDao(UserDao.class);
    final String authenticationKey = userDao.getAuthenticationToken(user.getId());
    final StringBuffer buf = new StringBuffer();
    buf.append("token=").append(authenticationKey);
    if (additionalParams != null) {
      buf.append(additionalParams);
    }
    final String encryptedParams = Registry.instance().getDao(UserDao.class).encrypt(buf.toString());
    final String result = "/export/ProjectForge.ics?user=" + user.getId() + "&q=" + encryptedParams;
    return result;
  }

  @Override
  protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
  {
    if (WebConfiguration.isUpAndRunning() == false) {
      log.error("System isn't up and running, CalendarFeed call denied. The system is may-be in start-up phase or in maintenance mode.");
      resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
      return;
    }
    PFUserDO user = null;
    String logMessage = null;
    try {
      MDC.put("ip", req.getRemoteAddr());
      MDC.put("session", req.getSession().getId());
      if (StringUtils.isBlank(req.getParameter("user")) || StringUtils.isBlank(req.getParameter("q"))) {
        resp.sendError(HttpStatus.SC_BAD_REQUEST);
        log.error("Bad request, parameters user and q not given. Query string is: " + req.getQueryString());
        return;
      }
      final String encryptedParams = req.getParameter("q");
      final Integer userId = NumberHelper.parseInteger(req.getParameter("user"));
      if (userId == null) {
        log.error("Bad request, parameter user is not an integer: " + req.getQueryString());
        return;
      }
      final Registry registry = Registry.instance();
      user = registry.getUserGroupCache().getUser(userId);
      if (user == null) {
        log.error("Bad request, user not found: " + req.getQueryString());
        return;
      }
      PFUserContext.setUser(user);
      MDC.put("user", user.getUsername());
      final String decryptedParams = registry.getDao(UserDao.class).decrypt(userId, encryptedParams);
      if (decryptedParams == null) {
        log.error("Bad request, can't decrypt parameter q (may-be the user's authentication token was changed): " + req.getQueryString());
        return;
      }
      final Map<String, String> params = StringHelper.getKeyValues(decryptedParams, "&");
      final Calendar calendar = createCal(params, userId, params.get("token"), params.get(PARAM_NAME_TIMESHEET_USER));
      final StringBuffer buf = new StringBuffer();
      boolean first = true;
      for (final Map.Entry<String, String> entry : params.entrySet()) {
        if ("token".equals(entry.getKey()) == true) {
          continue;
        }
        first = StringHelper.append(buf, first, entry.getKey(), ", ");
        buf.append("=").append(entry.getValue());
      }
      logMessage = buf.toString();
      log.info("Getting calendar entries for: " + logMessage);

      if (calendar == null) {
        resp.sendError(HttpStatus.SC_BAD_REQUEST);
        log.error("Bad request, can't find calendar.");
        return;
      }

      resp.setContentType("text/calendar");
      final CalendarOutputter output = new CalendarOutputter(false);
      try {
        output.output(calendar, resp.getOutputStream());
      } catch (final ValidationException ex) {
        ex.printStackTrace();
      }
    } finally {
      log.info("Finished request: " + logMessage);
      PFUserContext.setUser(null);
      MDC.remove("ip");
      MDC.remove("session");
      if (user != null) {
        MDC.remove("user");
      }
    }
  }

  /**
   * creates a calendar for the user, identified by his name and authentication key.
   * @param params
   *
   * @param userName
   * @param userKey
   * @return a calendar, null if authentication fails
   */
  private Calendar createCal(final Map<String, String> params, final Integer userId, final String authKey, final String timesheetUserParam)
  {
    final UserDao userDao = Registry.instance().getDao(UserDao.class);
    final PFUserDO loggedInUser = userDao.getUserByAuthenticationToken(userId, authKey);

    if (loggedInUser == null) {
      return null;
    }
    PFUserDO timesheetUser = null;
    if (StringUtils.isNotBlank(timesheetUserParam) == true) {
      final Integer timesheetUserId = NumberHelper.parseInteger(timesheetUserParam);
      if (timesheetUserId != null) {
        if (timesheetUserId.equals(loggedInUser.getId()) == false) {
          log.error("Not yet allowed: all users are only allowed to download their own time-sheets.");
          return null;
        }
        timesheetUser = userDao.getUserGroupCache().getUser(timesheetUserId);
        if (timesheetUser == null) {
          log.error("Time-sheet user with id '" + timesheetUserParam + "' not found.");
          return null;
        }
      }
    }
    // creating a new calendar
    final Calendar calendar = new Calendar();
    final Locale locale = PFUserContext.getLocale();
    calendar.getProperties().add(
        new ProdId("-//" + loggedInUser.getDisplayUsername() + "//ProjectForge//" + locale.toString().toUpperCase()));
    calendar.getProperties().add(Version.VERSION_2_0);
    calendar.getProperties().add(CalScale.GREGORIAN);

    // setup event is needed for empty calendars
    calendar.getComponents().add(new VEvent(new net.fortuna.ical4j.model.Date(0), SETUP_EVENT));

    // adding events
    for (final VEvent event : getEvents(params, timesheetUser)) {
      calendar.getComponents().add(event);
    }
    return calendar;
  }

  /**
   * builds the list of events
   *
   * @return
   */
  private List<VEvent> getEvents(final Map<String, String> params, PFUserDO timesheetUser)
  {
    final PFUserDO loggedInUser = PFUserContext.getUser();
    if (loggedInUser == null) {
      throw new AccessException("No logged-in-user found!");
    }
    final List<VEvent> events = new ArrayList<VEvent>();
    final TimeZone timezone = ICal4JUtils.getUserTimeZone();
    final java.util.Calendar cal = java.util.Calendar.getInstance(PFUserContext.getTimeZone());

    boolean eventsExist = false;
    for (final CalendarFeedHook hook : feedHooks) {
      final List<VEvent> list = hook.getEvents(params, timezone);
      if (list != null && list.size() > 0) {
        events.addAll(list);
        eventsExist = true;
      }
    }

    if (timesheetUser != null) {
      if (loggedInUser.getId().equals(timesheetUser.getId()) == false && isOtherUsersAllowed() == false) {
        // Only project managers, controllers and administrative staff is allowed to subscribe time-sheets of other users.
        log.warn("User tried to get time-sheets of other user: " + timesheetUser);
        timesheetUser = loggedInUser;
      }
      // initializes timesheet filter
      final TimesheetFilter filter = new TimesheetFilter();
      filter.setUserId(timesheetUser.getId());
      filter.setDeleted(false);
      filter.setStopTime(cal.getTime());
      // calculates the offset of the calendar
      final int offset = cal.get(java.util.Calendar.MONTH) - PERIOD_IN_MONTHS;
      if (offset < 0) {
        setCalDate(cal, cal.get(java.util.Calendar.YEAR) - 1, 12 + offset);
      } else {
        setCalDate(cal, cal.get(java.util.Calendar.YEAR), offset);
      }
      filter.setStartTime(cal.getTime());

      final TimesheetDao timesheetDao = Registry.instance().getDao(TimesheetDao.class);
      final List<TimesheetDO> timesheetList = timesheetDao.getList(filter);

      // iterate over all timesheets and adds each event to the calendar
      for (final TimesheetDO timesheet : timesheetList) {
        final String uid = TeamCalConfig.get().createTimesheetUid(timesheet.getId());
        String summary;
        if (eventsExist == true) {
          summary = TimesheetEventsProvider.getTitle(timesheet) + " (ts)";
        } else {
          summary = TimesheetEventsProvider.getTitle(timesheet);
        }
        final VEvent vEvent = ICal4JUtils.createVEvent(timesheet.getStartTime(), timesheet.getStopTime(), uid, summary);
        if (StringUtils.isNotBlank(timesheet.getDescription()) == true) {
          vEvent.getProperties().add(new Description(timesheet.getDescription()));
        }
        if (StringUtils.isNotBlank(timesheet.getLocation()) == true) {
          vEvent.getProperties().add(new Location(timesheet.getLocation()));
        }
        events.add(vEvent);
      }
    }
    final String holidays = params.get(PARAM_NAME_HOLIDAYS);
    if ("true".equals(holidays) == true) {
      final HolidayEventsProvider holidaysEventsProvider = new HolidayEventsProvider();
      DateTime holidaysFrom = new DateTime(PFUserContext.getDateTimeZone());
      holidaysFrom = holidaysFrom.dayOfYear().withMinimumValue().millisOfDay().withMinimumValue().minusYears(2);
      final DateTime holidayTo = holidaysFrom.plusYears(6);
      for (final Event event : holidaysEventsProvider.getEvents(holidaysFrom, holidayTo)) {
        final Date fromDate = event.getStart().toDate();
        final Date toDate = event.getEnd() != null ? event.getEnd().toDate() : fromDate;
        final VEvent vEvent = ICal4JUtils.createVEvent(fromDate, toDate, "pf-holiday" + event.getId(), event.getTitle(), true);
        events.add(vEvent);
      }
    }
    final String weeksOfYear = params.get(PARAM_NAME_WEEK_OF_YEARS);
    if ("true".equals(weeksOfYear) == true) {
      final DayHolder from = new DayHolder();
      from.setBeginOfYear().add(java.util.Calendar.YEAR, -2).setBeginOfWeek();
      final DayHolder to = new DayHolder(from);
      to.add(java.util.Calendar.YEAR, 6);
      final DayHolder current = new DayHolder(from);
      int paranoiaCounter = 0;
      do {
        final VEvent vEvent = ICal4JUtils.createVEvent(current.getDate(), current.getDate(), "pf-weekOfYear"
            + current.getYear()
            + "-"
            + paranoiaCounter, PFUserContext.getLocalizedString("calendar.weekOfYearShortLabel") + " " + current.getWeekOfYear(), true);
        events.add(vEvent);
        current.add(java.util.Calendar.WEEK_OF_YEAR, 1);
        if (++paranoiaCounter > 500) {
          log.warn("Dear developer, please have a look here, paranoiaCounter exceeded! Aborting calculation of weeks of year.");
        }
      } while (current.before(to) == true);
    }
    // Integer hrPlanningUserId = NumberHelper.parseInteger(params.get(PARAM_NAME_HR_PLANNING));
    // if (hrPlanningUserId != null) {
    // if (loggedInUser.getId().equals(hrPlanningUserId) == false && isOtherUsersAllowed() == false) {
    // // Only project managers, controllers and administrative staff is allowed to subscribe time-sheets of other users.
    // log.warn("User tried to get time-sheets of other user: " + timesheetUser);
    // hrPlanningUserId = loggedInUser.getId();
    // }
    // final HRPlanningDao hrPlanningDao = Registry.instance().getDao(HRPlanningDao.class);
    // final HRPlanningEventsProvider hrPlanningEventsProvider = new HRPlanningEventsProvider(new CalendarFilter().setShowPlanning(true)
    // .setTimesheetUserId(hrPlanningUserId), hrPlanningDao);
    // DateTime planningFrom = new DateTime(PFUserContext.getDateTimeZone());
    // planningFrom = planningFrom.dayOfYear().withMinimumValue().millisOfDay().withMinimumValue().minusYears(1);
    // final DateTime planningTo = planningFrom.plusYears(4);
    // for (final Event event : hrPlanningEventsProvider.getEvents(planningFrom, planningTo)) {
    // final Date fromDate = event.getStart().toDate();
    // final Date toDate = event.getEnd() != null ? event.getEnd().toDate() : fromDate;
    // final VEvent vEvent = ICal4JUtils.createVEvent(fromDate, toDate, "pf-hr-planning" + event.getId(), event.getTitle(), true);
    // events.add(vEvent);
    // }
    // }
    return events;
  }

  /**
   * sets the calendar to a special date. Used to calculate the year offset of an negative time period. When the time period is set to 4
   * month and the current month is at the begin of a year, the year-number must be decremented by one
   *
   * @param cal
   * @param year
   * @param mounth
   */
  private void setCalDate(final java.util.Calendar cal, final int year, final int mounth)
  {
    cal.clear();
    cal.set(java.util.Calendar.YEAR, year);
    cal.set(java.util.Calendar.MONTH, mounth);
  }

  private boolean isOtherUsersAllowed()
  {
    return UserRights.getAccessChecker().isLoggedInUserMemberOfGroup(ProjectForgeGroup.FINANCE_GROUP, ProjectForgeGroup.CONTROLLING_GROUP,
        ProjectForgeGroup.PROJECT_MANAGER);
  }

  public static void registerFeedHook(final CalendarFeedHook hook)
  {
    feedHooks.add(hook);
  }

}
TOP

Related Classes of org.projectforge.web.calendar.CalendarFeed

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.