/** * Copyright (c) 2008-2012 The Sakai Foundation * * Licensed under the Educational Community 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.osedu.org/licenses/ECL-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.sakaiproject.profile2.tool.pages.panels; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxEventBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextArea; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.spring.injection.annot.SpringBean; import org.sakaiproject.profile2.logic.ProfileConnectionsLogic; import org.sakaiproject.profile2.logic.ProfileMessagingLogic; import org.sakaiproject.profile2.logic.SakaiProxy; import org.sakaiproject.profile2.model.Person; import org.sakaiproject.profile2.tool.components.ResourceReferences; import org.sakaiproject.profile2.tool.models.NewMessageModel; import org.sakaiproject.profile2.util.ProfileConstants; import org.sakaiproject.profile2.util.ProfileUtils; import org.wicketstuff.objectautocomplete.AutoCompletionChoicesProvider; import org.wicketstuff.objectautocomplete.ObjectAutoCompleteBuilder; import org.wicketstuff.objectautocomplete.ObjectAutoCompleteField; import org.wicketstuff.objectautocomplete.ObjectAutoCompleteRenderer; public class ComposeNewMessage extends Panel { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(ComposeNewMessage.class); @SpringBean(name="org.sakaiproject.profile2.logic.SakaiProxy") private SakaiProxy sakaiProxy; @SpringBean(name="org.sakaiproject.profile2.logic.ProfileMessagingLogic") protected ProfileMessagingLogic messagingLogic; @SpringBean(name="org.sakaiproject.profile2.logic.ProfileConnectionsLogic") protected ProfileConnectionsLogic connectionsLogic; private TextField<String> toField; private Label formFeedback; public ComposeNewMessage(String id) { super(id); //current user final String userId = sakaiProxy.getCurrentUserId(); //setup model NewMessageModel newMessage = new NewMessageModel(); newMessage.setFrom(userId); //feedback for form submit action formFeedback = new Label("formFeedback"); formFeedback.setOutputMarkupPlaceholderTag(true); add(formFeedback); //setup form final Form<NewMessageModel> form = new Form<NewMessageModel>("form", new Model<NewMessageModel>(newMessage)); //close button /* WebMarkupContainer closeButton = new WebMarkupContainer("closeButton"); closeButton.add(new AjaxFallbackLink<Void>("link") { private static final long serialVersionUID = 1L; public void onClick(AjaxRequestTarget target) { if(target != null) { target.prependJavascript("$('#" + thisPanel.getMarkupId() + "').slideUp();"); target.appendJavascript("setMainFrameHeight(window.name);"); } } }.add(new ContextImage("img",new Model<String>(ProfileConstants.CLOSE_IMAGE)))); form.add(closeButton); */ //to label form.add(new Label("toLabel", new ResourceModel("message.to"))); //get connections final List<Person> connections = connectionsLogic.getConnectionsForUser(userId); Collections.sort(connections); // list provider AutoCompletionChoicesProvider<Person> provider = new AutoCompletionChoicesProvider<Person>() { private static final long serialVersionUID = 1L; public Iterator<Person> getChoices(String input) { return connectionsLogic.getConnectionsSubsetForSearch(connections, input, true).iterator(); } }; //renderer ObjectAutoCompleteRenderer<Person> renderer = new ObjectAutoCompleteRenderer<Person>(){ private static final long serialVersionUID = 1L; protected String getIdValue(Person p) { return p.getUuid(); } protected String getTextValue(Person p) { return p.getDisplayName(); } }; //autocompletefield builder ObjectAutoCompleteBuilder<Person,String> builder = new ObjectAutoCompleteBuilder<Person,String>(provider); builder.autoCompleteRenderer(renderer); builder.searchLinkImage(ResourceReferences.CROSS_IMG_LOCAL); builder.preselect(); //autocompletefield final ObjectAutoCompleteField<Person, String> autocompleteField = builder.build("toField", new PropertyModel<String>(newMessage, "to")); toField = autocompleteField.getSearchTextField(); toField.setMarkupId("messagerecipientinput"); toField.setOutputMarkupId(true); toField.add(new AttributeModifier("class", true, new Model<String>("formInputField"))); toField.setRequired(true); form.add(autocompleteField); //subject form.add(new Label("subjectLabel", new ResourceModel("message.subject"))); final TextField<String> subjectField = new TextField<String>("subjectField", new PropertyModel<String>(newMessage, "subject")); subjectField.setMarkupId("messagesubjectinput"); subjectField.setOutputMarkupId(true); subjectField.add(new RecipientEventBehavior("onfocus")); form.add(subjectField); //body form.add(new Label("messageLabel", new ResourceModel("message.message"))); final TextArea<String> messageField = new TextArea<String>("messageField", new PropertyModel<String>(newMessage, "message")); messageField.setMarkupId("messagebodyinput"); messageField.setOutputMarkupId(true); messageField.setRequired(true); messageField.add(new RecipientEventBehavior("onfocus")); form.add(messageField); //send button IndicatingAjaxButton sendButton = new IndicatingAjaxButton("sendButton", form) { private static final long serialVersionUID = 1L; protected void onSubmit(AjaxRequestTarget target, Form form) { //get the backing model NewMessageModel newMessage = (NewMessageModel) form.getModelObject(); //generate the thread id String threadId = ProfileUtils.generateUuid(); //save it, it will be abstracted into its proper parts and email notifications sent if(messagingLogic.sendNewMessage(newMessage.getTo(), newMessage.getFrom(), threadId, newMessage.getSubject(), newMessage.getMessage())) { //post event sakaiProxy.postEvent(ProfileConstants.EVENT_MESSAGE_SENT, "/profile/" + newMessage.getFrom(), true); //success formFeedback.setDefaultModel(new ResourceModel("success.message.send.ok")); formFeedback.add(new AttributeModifier("class", true, new Model<String>("success"))); //target.appendJavascript("$('#" + form.getMarkupId() + "').slideUp();"); target.appendJavascript("setMainFrameHeight(window.name);"); //PRFL-797 all fields when successful, to prevent multiple messages. //User can just click Compose message again to get a new form this.setEnabled(false); autocompleteField.setEnabled(false); subjectField.setEnabled(false); messageField.setEnabled(false); target.addComponent(this); target.addComponent(autocompleteField); target.addComponent(subjectField); target.addComponent(messageField); } else { //error formFeedback.setDefaultModel(new ResourceModel("error.message.send.failed")); formFeedback.add(new AttributeModifier("class", true, new Model<String>("alertMessage"))); } formFeedback.setVisible(true); target.addComponent(formFeedback); } protected void onError(AjaxRequestTarget target, Form form) { //check which item didn't validate and update the feedback model if(!toField.isValid()) { formFeedback.setDefaultModel(new ResourceModel("error.message.required.to")); } if(!messageField.isValid()) { formFeedback.setDefaultModel(new ResourceModel("error.message.required.body")); } formFeedback.add(new AttributeModifier("class", true, new Model<String>("alertMessage"))); target.addComponent(formFeedback); } }; form.add(sendButton); sendButton.setModel(new ResourceModel("button.message.send")); add(form); } /** * Inner class to provide simple validation of the to field and print an error message * if it's not set. This is a bit of a hack since it should be on the toField, * but we can't overload the onchange/onblur event of the toField itself as that event is already taken. * * Would need to reimplement autocompletebox as plain javascript. * * @author Steve Swinsburg (steve.swinsburg@gmail.com) * */ class RecipientEventBehavior extends AjaxEventBehavior { private static final long serialVersionUID = 1L; public RecipientEventBehavior(String event) { super(event); } @Override protected void onEvent(AjaxRequestTarget target) { if(StringUtils.isBlank(toField.getValue())) { formFeedback.setDefaultModel(new ResourceModel("error.message.required.to")); formFeedback.add(new AttributeModifier("class", true, new Model<String>("alertMessage"))); } else { formFeedback.setVisible(false); } target.addComponent(formFeedback); } } }