/**
* 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 org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
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.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
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.ProfileMessagingLogic;
import org.sakaiproject.profile2.logic.ProfilePreferencesLogic;
import org.sakaiproject.profile2.logic.ProfilePrivacyLogic;
import org.sakaiproject.profile2.logic.SakaiProxy;
import org.sakaiproject.profile2.model.Message;
import org.sakaiproject.profile2.model.MessageParticipant;
import org.sakaiproject.profile2.model.MessageThread;
import org.sakaiproject.profile2.model.ProfilePreferences;
import org.sakaiproject.profile2.model.ProfilePrivacy;
import org.sakaiproject.profile2.tool.components.ProfileImageRenderer;
import org.sakaiproject.profile2.tool.dataproviders.MessagesDataProvider;
import org.sakaiproject.profile2.tool.models.StringModel;
import org.sakaiproject.profile2.tool.pages.MyMessages;
import org.sakaiproject.profile2.tool.pages.ViewProfile;
import org.sakaiproject.profile2.util.ProfileConstants;
import org.sakaiproject.profile2.util.ProfileUtils;
public class MessageView extends Panel {
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(ConfirmedFriends.class);
private DataView<Message> messageList = null;
private WebMarkupContainer messageListContainer = null;
private boolean lastUnreadSet = false;
@SpringBean(name="org.sakaiproject.profile2.logic.SakaiProxy")
protected SakaiProxy sakaiProxy;
@SpringBean(name="org.sakaiproject.profile2.logic.ProfilePreferencesLogic")
protected ProfilePreferencesLogic preferencesLogic;
@SpringBean(name="org.sakaiproject.profile2.logic.ProfileMessagingLogic")
protected ProfileMessagingLogic messagingLogic;
@SpringBean(name="org.sakaiproject.profile2.logic.ProfilePrivacyLogic")
protected ProfilePrivacyLogic privacyLogic;
/**
* Constructor for an incoming link with a threadId as part of the PageParameters
* @param parameters
*/
public MessageView(final String id, PageParameters parameters) {
super(id);
log.debug("MyMessageView(" + parameters.toString() +")");
MessageThread thread = messagingLogic.getMessageThread(parameters.getString("thread"));
//check user is a thread participant
String currentUserUuid = sakaiProxy.getCurrentUserId();
if(!messagingLogic.isThreadParticipant(thread.getId(), currentUserUuid)) {
//this would only ever happen if the user has access to the other user's workspace because the link is a direct link to their site
//so they won't even reach this part if they don't have access - so it would need to be a very special case.
log.error("MyMessageView: user " + currentUserUuid + " attempted to access restricted thread: " + thread.getId());
throw new RestartResponseException(new MyMessages());
}
renderMyMessagesView(sakaiProxy.getCurrentUserId(), thread.getId(), thread.getSubject());
}
/**
* Constructor for normal viewing
* @param currentUserUuid
* @param threadId
*/
public MessageView(final String id, final String currentUserUuid, final String threadId) {
super(id);
log.debug("MyMessageView(" + currentUserUuid + ", " + threadId + ")");
//get subject
String threadSubject = messagingLogic.getThreadSubject(threadId);
renderMyMessagesView(currentUserUuid, threadId, threadSubject);
}
/**
* Constructor for normal viewing
* @param currentUserUuid
* @param threadId
*/
public MessageView(final String id, final String currentUserUuid, final String threadId, final String threadSubject) {
super(id);
log.debug("MyMessageView(" + currentUserUuid + ", " + threadId + ", " + threadSubject + ")");
renderMyMessagesView(currentUserUuid, threadId, threadSubject);
}
/**
* Does the actual rendering of the page
* @param currentUserUuid
* @param threadId
* @param threadSubject
*/
private void renderMyMessagesView(final String currentUserUuid, final String threadId, final String threadSubject) {
//details container
WebMarkupContainer messageDetailsContainer = new WebMarkupContainer("messageDetailsContainer");
messageDetailsContainer.setOutputMarkupId(true);
//thread subject
Label threadSubjectLabel = new Label("threadSubject", new Model<String>(threadSubject));
messageDetailsContainer.add(threadSubjectLabel);
add(messageDetailsContainer);
//list container
messageListContainer = new WebMarkupContainer("messageListContainer");
messageListContainer.setOutputMarkupId(true);
//get our list of messages
final MessagesDataProvider provider = new MessagesDataProvider(threadId);
messageList = new DataView<Message>("messageList", provider) {
private static final long serialVersionUID = 1L;
protected void populateItem(final Item<Message> item) {
final Message message = (Message)item.getDefaultModelObject();
final String messageFromUuid = message.getFrom();
//we need to know if this message has been read or not so we can style it accordingly
//we only need this if we didn't send the message
MessageParticipant participant = null;
boolean messageOwner = false;
if(StringUtils.equals(messageFromUuid, currentUserUuid)) {
messageOwner = true;
}
if(!messageOwner) {
participant = messagingLogic.getMessageParticipant(message.getId(), currentUserUuid);
}
//prefs and privacy
ProfilePreferences prefs = preferencesLogic.getPreferencesRecordForUser(messageFromUuid);
ProfilePrivacy privacy = privacyLogic.getPrivacyRecordForUser(messageFromUuid);
//photo link
AjaxLink<String> photoLink = new AjaxLink<String>("photoLink", new Model<String>(messageFromUuid)) {
private static final long serialVersionUID = 1L;
public void onClick(AjaxRequestTarget target) {
setResponsePage(new ViewProfile(getModelObject()));
}
};
//photo
photoLink.add(new ProfileImageRenderer("messagePhoto", messageFromUuid, prefs, privacy, ProfileConstants.PROFILE_IMAGE_THUMBNAIL, false));
item.add(photoLink);
//name link
AjaxLink<String> messageFromLink = new AjaxLink<String>("messageFromLink", new Model<String>(messageFromUuid)) {
private static final long serialVersionUID = 1L;
public void onClick(AjaxRequestTarget target) {
setResponsePage(new ViewProfile(getModelObject()));
}
};
messageFromLink.add(new Label("messageFromName", new Model<String>(sakaiProxy.getUserDisplayName(messageFromUuid))));
item.add(messageFromLink);
//date
item.add(new Label("messageDate", ProfileUtils.convertDateToString(message.getDatePosted(), ProfileConstants.MESSAGE_DISPLAY_DATE_FORMAT)));
//message body
item.add(new Label("messageBody", new Model<String>(message.getMessage())));
//highlight if new, then mark it as read
if(!messageOwner && !participant.isRead()) {
item.add(new AttributeAppender("class", true, new Model<String>("unread-message"), " "));
messagingLogic.toggleMessageRead(participant, true);
//set param for first unread message in the thread
if(!lastUnreadSet) {
lastUnreadSet=true;
item.add(new AttributeModifier("rel", true, new Model<String>("lastUnread")));
}
}
item.setOutputMarkupId(true);
}
};
messageList.setOutputMarkupId(true);
messageListContainer.add(messageList);
add(messageListContainer);
//reply form
StringModel stringModel = new StringModel();
Form<StringModel> replyForm = new Form<StringModel>("replyForm", new Model<StringModel>(stringModel));
//form feedback
final Label formFeedback = new Label("formFeedback");
formFeedback.setOutputMarkupPlaceholderTag(true);
add(formFeedback);
//reply field
replyForm.add(new Label("replyLabel", new ResourceModel("message.reply")));
final TextArea<StringModel> replyField = new TextArea<StringModel>("replyField", new PropertyModel<StringModel>(stringModel, "string"));
replyField.setRequired(true);
replyField.setOutputMarkupId(true);
replyForm.add(replyField);
//reply button
IndicatingAjaxButton replyButton = new IndicatingAjaxButton("replyButton", replyForm) {
private static final long serialVersionUID = 1L;
protected void onSubmit(AjaxRequestTarget target, Form form) {
StringModel stringModel = (StringModel) form.getModelObject();
String reply = stringModel.getString();
//create a direct link to view this message thread
//String messageLink = sakaiProxy.getDirectUrlToUserProfile(newMessage.getTo(), urlFor(MyMessageView.class, new PageParameters("thread=" + threadId)).toString());
//send it, get Message back so we can add it to the list
Message message = messagingLogic.replyToThread(threadId, reply, currentUserUuid);
if(message != null) {
//clear this field
replyField.setModelObject(null);
target.addComponent(replyField);
//create new item and add it to the list
//do we need to register this with the listview?
Component item = buildItem(message);
target.prependJavascript(String.format(
"var item=document.createElement('%s');item.id='%s';Wicket.$('%s').appendChild(item);",
"tr", item.getMarkupId(), messageListContainer.getMarkupId()));
target.addComponent(item);
//repaint the list of messages in this thread
//target.addComponent(messageListContainer);
//resize
target.appendJavascript("setMainFrameHeight(window.name);");
}
}
protected void onError(AjaxRequestTarget target, Form form) {
//validate
if(!replyField.isValid()) {
formFeedback.setDefaultModel(new ResourceModel("error.message.required.body"));
}
formFeedback.add(new AttributeModifier("class", true, new Model<String>("alertMessage")));
target.addComponent(formFeedback);
}
};
replyForm.add(replyButton);
replyButton.setModel(new ResourceModel("button.message.send"));
add(replyForm);
}
//build a single item in the list so we can add it to the end
private Component buildItem(Message message){
WebMarkupContainer item = new WebMarkupContainer(messageList.newChildId());
item.setOutputMarkupId(true);
messageList.add(item);
ProfilePreferences prefs = preferencesLogic.getPreferencesRecordForUser(message.getFrom());
//photo and link
item.add(new AjaxLink<String>("photoLink", new Model<String>(message.getFrom())) {
private static final long serialVersionUID = 1L;
public void onClick(AjaxRequestTarget target) {
setResponsePage(new ViewProfile(getModelObject()));
}
}.add(new ProfileImageRenderer("messagePhoto", message.getFrom(), prefs, ProfileConstants.PROFILE_IMAGE_THUMBNAIL, false)));
//name link
item.add(new AjaxLink<String>("messageFromLink", new Model<String>(message.getFrom())) {
private static final long serialVersionUID = 1L;
public void onClick(AjaxRequestTarget target) {
setResponsePage(new ViewProfile(getModelObject()));
}
}.add(new Label("messageFromName", new Model<String>(sakaiProxy.getUserDisplayName(message.getFrom())))));
//date
item.add(new Label("messageDate", ProfileUtils.convertDateToString(message.getDatePosted(), ProfileConstants.MESSAGE_DISPLAY_DATE_FORMAT)));
//message body
item.add(new Label("messageBody", new Model<String>(message.getMessage())));
return item;
}
}