blob: d7caa3a30eaa3b456be993043c7301420a6a26a5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.openmeetings.web.user.calendar;
import static org.apache.openmeetings.web.app.WebSession.getUserId;
import static org.apache.openmeetings.web.common.confirmation.ConfirmationHelper.newOkCancelDangerConfirm;
import java.util.List;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.db.entity.calendar.Appointment;
import org.apache.openmeetings.db.entity.calendar.OmCalendar;
import org.apache.openmeetings.service.calendar.caldav.AppointmentManager;
import org.apache.openmeetings.web.common.OmModalCloseButton;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.form.UrlTextField;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.validator.UrlValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxButton;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
/**
* Multipurpose Calendar Dialog form. This provides the ability to ask for a user prompt,
* for Creating, and Syncing of Calendars. Along with that also,
* during the Creation and Deletion of Appointments.
*/
public class CalendarDialog extends Modal<OmCalendar> {
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(CalendarDialog.class);
private CalendarPanel calendarPanel;
private final NotificationPanel feedback = new NotificationPanel("feedback");
private BootstrapAjaxButton save;
private BootstrapAjaxLink<String> delete;
private UserCalendarForm form;
private List<OmCalendar> cals; //List of calendars for syncing
private int calIndex = 0;
@SpringBean
private UserDao userDao;
@SpringBean
private AppointmentDao apptDao;
@SpringBean
private AppointmentManager apptManager;
public enum DIALOG_TYPE {
UPDATE_CALENDAR,
SYNC_CALENDAR,
UPDATE_APPOINTMENT,
DELETE_APPOINTMENT
}
//Defines the current mode of in which the Dialog is functioning in
private DIALOG_TYPE type = DIALOG_TYPE.UPDATE_CALENDAR;
private Appointment appointment = null;
public CalendarDialog(String id, final CalendarPanel calendarPanel, CompoundPropertyModel<OmCalendar> model) {
super(id, model);
this.calendarPanel = calendarPanel;
}
@Override
protected void onInitialize() {
header(new ResourceModel("calendar.dialogTitle"));
form = new UserCalendarForm("calform", getModel());
add(form);
addButton(save = new BootstrapAjaxButton(BUTTON_MARKUP_ID, new ResourceModel("144"), form, Buttons.Type.Outline_Primary) {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target) {
switch (type) {
case UPDATE_CALENDAR:
OmCalendar c = form.getModelObject();
c.setHref(form.url.getModelObject());
c.setOwner(userDao.get(c.getOwner().getId())); // owner might need to be refreshed
HttpClient client = calendarPanel.getHttpClient();
HttpClientContext context = calendarPanel.getHttpClientContext();
if (form.gcal.getModelObject()) {
c.setSyncType(OmCalendar.SyncType.GOOGLE_CALENDAR);
c.setToken(form.username.getModelObject());
if (c.getId() == null) {
calendarPanel.populateGoogleCalendar(c, target);
}
} else if (c.getId() == null && form.username.getModelObject() != null) {
apptManager.provideCredentials(context, c, new UsernamePasswordCredentials(form.username.getModelObject(),
form.pass.getModelObject()));
}
apptManager.createCalendar(client, context, c);
calendarPanel.refreshCalendars(target);
calendarPanel.refresh(target);
break;
case SYNC_CALENDAR:
syncCalendar(form.getModelObject(), target);
if (setFormModelObject()) {
setButtons(target);
target.add(show(target));
}
break;
case UPDATE_APPOINTMENT:
updateAppointment(appointment);
calendarPanel.refresh(target);
break;
case DELETE_APPOINTMENT:
deleteAppointment(appointment);
calendarPanel.refresh(target);
break;
}
clearFormModel(target);
target.add(feedback);
}
@Override
protected void onError(AjaxRequestTarget target) {
target.add(feedback);
}
});
save.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
delete = new BootstrapAjaxLink<>(BUTTON_MARKUP_ID, null, Buttons.Type.Outline_Danger, new ResourceModel("80")) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget handler) {
apptManager.deleteCalendar(form.getModelObject());
calendarPanel.refresh(handler);
calendarPanel.refreshCalendars(handler);
}
};
delete.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
addButton(delete.add(newOkCancelDangerConfirm(this, getString("833"))));
addButton(OmModalCloseButton.of());
super.onInitialize();
}
/**
* Open the Dialog with a specific type of Appointment Based Prompts
*
* @param handler - the {@link IPartialPageRequestHandler}
* @param type - the {@link DIALOG_TYPE} being opened
* @param a - the {@link Appointment}
*/
public void show(IPartialPageRequestHandler handler, DIALOG_TYPE type, Appointment a) {
this.type = type;
appointment = a;
if (isOwner(a)) {
if (setFormModelObject(a, handler)) {
handler.add(show(handler));
} else {
switch (type) {
case UPDATE_APPOINTMENT:
updateAppointment(a);
break;
case DELETE_APPOINTMENT:
deleteAppointment(a);
break;
case SYNC_CALENDAR, UPDATE_CALENDAR:
break;
default:
break;
}
calendarPanel.refresh(handler);
}
}
}
/**
* Open the Dialog with a specific type of Calendar Based Prompts
*
* @param handler - the {@link IPartialPageRequestHandler}
* @param type - the {@link DIALOG_TYPE} being opened
* @param c - the {@link OmCalendar}
*/
public void show(IPartialPageRequestHandler handler, DIALOG_TYPE type, OmCalendar c) {
this.type = type;
switch (type) {
case UPDATE_CALENDAR:
setFormModelObject(c);
setButtons(handler);
show(handler);
break;
case SYNC_CALENDAR:
if (setCalendarList(handler)) {
show(handler);
handler.add(this);
} else {
calendarPanel.refresh(handler);
}
break;
case DELETE_APPOINTMENT, UPDATE_APPOINTMENT:
break;
default:
break;
}
}
/**
* Performs syncing of the Calendar.
*
* @param c Calendar to sync
* @param handler Handler used to update the CalendarPanel
*/
private void syncCalendar(OmCalendar c, IPartialPageRequestHandler handler) {
HttpClient client = calendarPanel.getHttpClient();
HttpClientContext context = calendarPanel.getHttpClientContext();
if (form.username.getModelObject() != null) {
apptManager.provideCredentials(context, c, new UsernamePasswordCredentials(form.username.getModelObject(),
form.pass.getModelObject()));
}
apptManager.syncItem(client, context, c);
calendarPanel.refresh(handler);
log.trace("Calendar {} Successfully synced.", c.getTitle());
}
/**
* Performs Deletion of Appointment on the Calendar.
*
* @param a Appointment to delete
*/
private void deleteAppointment(Appointment a) {
apptManager.deleteItem(calendarPanel.getHttpClient(), calendarPanel.getHttpClientContext(), a);
appointment = null;
}
/**
* Performs updation of Appointment on the Calendar.
*
* @param a Appointment to update
*/
private void updateAppointment(Appointment a) {
apptManager.updateItem(calendarPanel.getHttpClient(), calendarPanel.getHttpClientContext(), a);
appointment = null;
}
private static boolean isOwner(Appointment object) {
return object.getOwner() != null && getUserId().equals(object.getOwner().getId());
}
private static boolean isOwner(OmCalendar object) {
return object.getOwner() != null && getUserId().equals(object.getOwner().getId());
}
/**
* Sets the calendar list to sync and
* syncs all them until a calendar whose
*
* @param target Ajax target to update the buttons.
* @return <code>true</code> if a Calendar needs to be synced
* else all calendars should have gotten synced
*/
private boolean setCalendarList(IPartialPageRequestHandler target) {
type = DIALOG_TYPE.SYNC_CALENDAR;
cals = apptManager.getCalendars(getUserId());
calendarPanel.getHttpClient();
calIndex = 0;
setButtons(target);
return setFormModelObject();
}
// Sets the form object when in need of syncing. Returns true if model is set
private boolean setFormModelObject() {
if (cals != null && !cals.isEmpty() && calIndex < cals.size()) {
OmCalendar calendar = cals.get(calIndex++);
HttpClient client = calendarPanel.getHttpClient();
HttpClientContext context = calendarPanel.getHttpClientContext();
if (!apptManager.testConnection(client, context, calendar)) {
form.setModelObject(calendar);
form.url.setModelObject(calendar.getHref());
return true;
} else {
apptManager.syncItem(client, context, calendar);
return setFormModelObject();
}
}
cals = null;
return false;
}
// Sets the form model object if the calendar cannot be reached. Returns true if model is set
private boolean setFormModelObject(Appointment a, IPartialPageRequestHandler target) {
OmCalendar c = a.getCalendar();
if (apptManager.testConnection(calendarPanel.getHttpClient(), calendarPanel.getHttpClientContext(), c)) {
return false;
}
setFormModelObject(c);
setButtons(target);
return true;
}
private void setFormModelObject(OmCalendar c) {
if (c != null) {
form.setModelObject(c);
form.url.setModelObject(c.getHref());
if (c.getSyncType() == OmCalendar.SyncType.GOOGLE_CALENDAR) {
form.username.setDefaultModelObject(c.getToken());
form.gcal.setDefaultModelObject(true);
} else {
form.gcal.setDefaultModelObject(false);
}
}
}
public void setButtons(IPartialPageRequestHandler target) {
switch (type) {
case UPDATE_APPOINTMENT, DELETE_APPOINTMENT, SYNC_CALENDAR:
target.add(delete.setVisible(false), save.setVisible(true));
break;
case UPDATE_CALENDAR:
OmCalendar c = form.getModelObject();
if (c.getId() == null) {
target.add(delete.setVisible(false));
} else {
target.add(delete.setVisible(isOwner(c)));
}
target.add(save.setVisible(isOwner(c)));
}
}
private void clearFormModel(IPartialPageRequestHandler handler) {
form.clearInput();
form.username.setModelObject(null);
handler.add(form);
}
private class UserCalendarForm extends Form<OmCalendar> {
private static final long serialVersionUID = 1L;
private TextField<String> username = new TextField<>("login", Model.of(""));
private PasswordTextField pass = new PasswordTextField("password", Model.of(""));
RequiredTextField<String> title = new RequiredTextField<>("title");
// Fields required for adding Google Calendar
private Label urlLabel;
private Label userLabel;
private Label passLabel;
AjaxCheckBox gcal; // Checkbox for Google Calendar
UrlTextField url = new UrlTextField("url", Model.of(""), new UrlValidator() {
private static final long serialVersionUID = 1L;
//Custom UrlValidator
@Override
public void validate(IValidatable<String> validatable) {
//Only Validate when It's not a Google Calendar i.e a URL
if (!gcal.getModelObject()) {
super.validate(validatable);
}
}
}) {
private static final long serialVersionUID = 1L;
@Override
protected String[] getInputTypes() {
//Can be URL or a Text
return new String[]{"url", "text"};
}
};
public UserCalendarForm(String id, IModel<OmCalendar> model) {
super(id, model);
setOutputMarkupId(true);
}
@Override
protected void onInitialize() {
urlLabel = new Label("urlLabel", getString("calendar.url"));
userLabel = new Label("userLabel", getString("114"));
passLabel = new Label("passLabel", getString("110"));
add(title);
add(feedback.setOutputMarkupId(true));
add(url.setRequired(true).setOutputMarkupId(true));
add(username.setOutputMarkupPlaceholderTag(true));
add(urlLabel.setOutputMarkupId(true));
add(pass.setRequired(false).setOutputMarkupPlaceholderTag(true));
add(userLabel.setOutputMarkupPlaceholderTag(true));
add(passLabel.setOutputMarkupPlaceholderTag(true));
gcal = new AjaxCheckBox("gcal", Model.of(false)) {
private static final long serialVersionUID = 1L;
//Checkbox, which when in "true" state will be in
// the Google Calendar State, otherwise in the CalDAV state
@Override
protected void onUpdate(AjaxRequestTarget target) {
setGCalVisibility(getModelObject());
target.add(UserCalendarForm.this);
}
};
add(gcal);
super.onInitialize();
}
@Override
protected void onModelChanged() {
OmCalendar calendar = getModelObject();
switch (type) {
case UPDATE_CALENDAR:
title.setEnabled(true);
if (calendar.getId() != null) {
url.setEnabled(false);
pass.setEnabled(false);
username.setEnabled(false);
gcal.setEnabled(false);
} else {
gcal.setEnabled(true);
url.setEnabled(true);
pass.setEnabled(true);
username.setEnabled(true);
}
setGCalVisibility(calendar);
break;
case SYNC_CALENDAR, UPDATE_APPOINTMENT, DELETE_APPOINTMENT:
title.setEnabled(false);
url.setEnabled(false);
username.setEnabled(true);
pass.setEnabled(true);
gcal.setEnabled(false);
setGCalVisibility(calendar);
break;
}
}
private void setGCalVisibility(OmCalendar calendar) {
setGCalVisibility(calendar.getSyncType() == OmCalendar.SyncType.GOOGLE_CALENDAR);
}
//Sets the visibility of the Labels and the TextFields based on
// whether it's a Google calendar or not.
private void setGCalVisibility(boolean isGoogleCalendar) {
if (isGoogleCalendar) {
gcal.setModelObject(true);
pass.setVisible(false);
passLabel.setVisible(false);
//Google Calendar ID
urlLabel.setDefaultModelObject(getString("calendar.googleID"));
url.setEnabled(true);
//Google API Key
userLabel.setDefaultModelObject(getString("calendar.googleKey"));
username.setEnabled(true);
} else {
gcal.setModelObject(false);
pass.setVisible(true);
passLabel.setVisible(true);
userLabel.setDefaultModelObject(getString("114"));
username.setModelObject(null);
urlLabel.setDefaultModelObject(getString("calendar.url"));
}
url.setLabel(Model.of((String)urlLabel.getDefaultModelObject()));
//Add new AttributeModifier to change the type of URLTextField, to text for
//Google Calendar and to URL for a normal CalDAV calendar
url.add(AttributeModifier.replace("type", gcal.getModelObject() ? "text" : "url"));
}
/**
* Validates the credentials and the server entered by the user by
* performing a HTTP Options Method.
* @see Form#onValidate()
*/
@Override
protected void onValidate() {
if (hasError()) {
return;
}
switch (type) {
case UPDATE_CALENDAR:
if (getModelObject().getId() != null || gcal.getModelObject()) {
return;
}
case UPDATE_APPOINTMENT, DELETE_APPOINTMENT, SYNC_CALENDAR:
try {
OmCalendar calendar = getModelObject();
if (url.isEnabled()) {
calendar.setHref(url.getInput());
}
HttpClient client = calendarPanel.getHttpClient();
HttpClientContext context = calendarPanel.getHttpClientContext();
apptManager.provideCredentials(context, calendar,
new UsernamePasswordCredentials(username.getInput(), pass.getInput()));
if (apptManager.testConnection(client, context, calendar)) {
return;
}
} catch (Exception e) {
log.error("Error executing the TestConnection", e);
}
error(getString("calendar.error"));
break;
default:
break;
}
}
}
}