1 package org.kuali.student.common.ui.client.widgets.commenttool;
2
3 import java.text.DateFormat;
4 import java.text.SimpleDateFormat;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Comparator;
8 import java.util.Date;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.kuali.student.common.ui.client.application.Application;
13 import org.kuali.student.common.ui.client.application.KSAsyncCallback;
14 import org.kuali.student.common.ui.client.configurable.mvc.HasReferenceId;
15 import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
16 import org.kuali.student.common.ui.client.mvc.Callback;
17 import org.kuali.student.common.ui.client.mvc.Controller;
18 import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
19 import org.kuali.student.common.ui.client.mvc.dto.ReferenceModel;
20 import org.kuali.student.common.ui.client.service.CommentRpcService;
21 import org.kuali.student.common.ui.client.service.CommentRpcServiceAsync;
22 import org.kuali.student.common.ui.client.widgets.KSButton;
23 import org.kuali.student.common.ui.client.widgets.KSLabel;
24 import org.kuali.student.common.ui.client.widgets.KSLightBox;
25 import org.kuali.student.common.ui.client.widgets.KSTextArea;
26 import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
27 import org.kuali.student.common.ui.client.widgets.buttongroups.OkGroup;
28 import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations.OkEnum;
29 import org.kuali.student.common.ui.client.widgets.dialog.ConfirmationDialog;
30 import org.kuali.student.common.ui.client.widgets.layout.VerticalFlowPanel;
31 import org.kuali.student.core.comment.dto.CommentInfo;
32 import org.kuali.student.core.dto.RichTextInfo;
33 import org.kuali.student.core.dto.StatusInfo;
34 import org.kuali.student.core.dto.DtoConstants.DtoState;
35
36 import com.google.gwt.core.client.GWT;
37 import com.google.gwt.dom.client.Style;
38 import com.google.gwt.event.dom.client.ClickEvent;
39 import com.google.gwt.event.dom.client.ClickHandler;
40 import com.google.gwt.event.dom.client.KeyUpEvent;
41 import com.google.gwt.event.dom.client.KeyUpHandler;
42 import com.google.gwt.user.client.Window;
43 import com.google.gwt.user.client.rpc.AsyncCallback;
44 import com.google.gwt.user.client.ui.FlexTable;
45 import com.google.gwt.user.client.ui.FlowPanel;
46 import com.google.gwt.user.client.ui.HTML;
47 import com.google.gwt.user.client.ui.HorizontalPanel;
48 import com.google.gwt.user.client.ui.Widget;
49
50 public class CommentTool implements HasReferenceId {
51
52 private CommentRpcServiceAsync commentServiceAsync = GWT.create(CommentRpcService.class);
53 private String referenceId;
54 private String referenceTypeKey;
55 private String referenceType;
56 private String referenceState;
57 private KSLightBox commentLightBox;
58 private HTML loggedInUserNameHTML = new HTML();
59 private String loggedInUserId;
60 private KSTextArea commentTextArea = new KSTextArea();
61 private KSButton cancelEditButton = new KSButton("Cancel");
62 private KSButton submitCommentButton = new KSButton("Submit");
63 private FlexTable commentsTableLayout = new FlexTable();
64 private static final DateFormat df = new SimpleDateFormat("MMMM dd, yyyy - hh:mmaaa");
65 private Controller controller;
66 private Enum<?> viewEnum;
67 private String viewName;
68 VerticalFlowPanel loggedInLabelsPanel = new VerticalFlowPanel();
69 VerticalFlowPanel commentEditPanel = new VerticalFlowPanel();
70 KSLabel notAuthorizedToAddComments = new KSLabel("The document must be saved before Comments can be added.");
71 private EditMode editMode = EditMode.ADD_COMMENT;
72 private CommentInfo selectedComment;
73 private List<Callback<EditMode>> editControlsCallbacks = new ArrayList<Callback<EditMode>>();
74 private String commentTypeKey;
75 private Map<String, String> referenceAttributes;
76 private KSLabel proposalTitle = new KSLabel();
77 private String title;
78
79 private enum EditMode {
80 ADD_COMMENT, UPDATE_COMMENT
81 }
82
83 public CommentTool(Enum<?> viewEnum, String viewName, String commentTypeKey, String title) {
84 this.viewName = viewName;
85 this.viewEnum = viewEnum;
86 this.commentTypeKey = commentTypeKey;
87 this.title = title;
88 init();
89
90 }
91
92 public Controller getController() {
93 return this.controller;
94 }
95
96
97 public void setController(Controller controller){
98 this.controller = controller;
99 }
100
101
102 public Widget asWidget(){
103 return commentLightBox.getWidget();
104 }
105
106 private OkGroup buttonPanel = new OkGroup(new Callback<OkEnum>(){
107
108 @Override
109 public void exec(OkEnum result) {
110 if(result == OkEnum.Ok){
111 }
112
113 }
114 });
115
116 private void init() {
117 commentLightBox = new KSLightBox("Comments");
118 VerticalFlowPanel contentPanel = new VerticalFlowPanel();
119
120 SectionTitle title = SectionTitle.generateH2Title(this.title);
121 title.addStyleName("ks-layout-header");
122 HTML htmlLabel = new HTML("<b>All comments posted here will be visible to authors, and " +
123 "to reviewers after you submit the proposal.</b>");
124 title.setStyleName("cluProposalTitleSection");
125 proposalTitle.setVisible(false);
126 contentPanel.add(proposalTitle);
127 contentPanel.add(title);
128 contentPanel.add(htmlLabel);
129
130
131 SectionTitle leaveACommentTitle = SectionTitle.generateH3Title("Leave a Comment");
132 leaveACommentTitle.getElement().getStyle().setProperty("borderBottom", "1px solid #D8D8D8");
133 leaveACommentTitle.getElement().getStyle().setProperty("marginTop", "2em");
134 contentPanel.add(leaveACommentTitle);
135
136
137 HTML loggedInAsLabel = new HTML("<b>Logged in as:<b/>");
138 loggedInLabelsPanel.add(loggedInAsLabel);
139 final String userId = Application.getApplicationContext().getUserId();
140 commentServiceAsync.getUserRealName(userId, new AsyncCallback<String>() {
141 @Override
142 public void onFailure(Throwable caught) {
143 loggedInUserNameHTML.setHTML("<b>" + userId + "</b>");
144 }
145 @Override
146 public void onSuccess(String result) {
147 if (result != null && !result.isEmpty()) {
148 loggedInUserNameHTML.setHTML("<b>" + result + "</b>");
149 } else {
150 loggedInUserNameHTML.setHTML("<b>" + userId + "</b>");
151 }
152 }
153 });
154
155 loggedInUserId = userId;
156 loggedInLabelsPanel.add(loggedInUserNameHTML);
157 commentTextArea.setSize("500", "100");
158 commentEditPanel.add(commentTextArea);
159 FlowPanel buttonsPanel = new FlowPanel();
160 buttonsPanel.add(cancelEditButton);
161 cancelEditButton.addClickHandler(new ClickHandler() {
162 @Override
163 public void onClick(ClickEvent event) {
164 setEditMode(EditMode.ADD_COMMENT);
165 }
166 });
167 buttonsPanel.add(submitCommentButton);
168 submitCommentButton.setEnabled(false);
169 commentEditPanel.add(buttonsPanel);
170 buttonsPanel.addStyleName("KS-Comment-Button-Panel");
171
172 commentTextArea.addKeyUpHandler(new KeyUpHandler() {
173 @Override
174 public void onKeyUp(KeyUpEvent event) {
175 submitCommentButton.setEnabled(true);
176 }
177 });
178
179
180
181 submitCommentButton.addClickHandler(new ClickHandler() {
182 @Override
183 public void onClick(ClickEvent event) {
184 switch (editMode) {
185 case ADD_COMMENT: {
186 CommentInfo newComment = new CommentInfo();
187
188 RichTextInfo text = new RichTextInfo();
189 text.setFormatted(commentTextArea.getText());
190 text.setPlain(commentTextArea.getText());
191 newComment.setType(commentTypeKey);
192 newComment.setReferenceId(referenceId);
193 newComment.setReferenceTypeKey(referenceTypeKey);
194 newComment.setState(DtoState.ACTIVE.toString());
195 newComment.setCommentText(text);
196
197 try {
198 commentServiceAsync.addComment(referenceId, referenceTypeKey, newComment, new KSAsyncCallback<CommentInfo>(){
199
200 @Override
201 public void handleFailure(Throwable caught) {
202 GWT.log("Add Comment Failed", caught);
203 }
204
205 @Override
206 public void onSuccess(CommentInfo result) {
207 refreshComments();
208 }
209 });
210 } catch (Exception e) {
211 GWT.log("Add Comment Failed", e);
212 }
213 break;
214 }
215 case UPDATE_COMMENT: {
216 if (selectedComment != null) {
217 RichTextInfo text = new RichTextInfo();
218 text.setFormatted(commentTextArea.getText());
219 text.setPlain(commentTextArea.getText());
220 selectedComment.setReferenceId(referenceId);
221 selectedComment.setReferenceTypeKey(referenceTypeKey);
222 selectedComment.setCommentText(text);
223
224 selectedComment.setType(commentTypeKey);
225 }
226
227 try {
228 commentServiceAsync.updateComment(referenceId, referenceTypeKey, selectedComment, new KSAsyncCallback<CommentInfo>(){
229
230 @Override
231 public void handleFailure(Throwable caught) {
232 GWT.log("Add Comment Failed", caught);
233 }
234
235 @Override
236 public void onSuccess(CommentInfo result) {
237 refreshComments();
238 setEditMode(EditMode.ADD_COMMENT);
239 }
240 });
241 } catch (Exception e) {
242 GWT.log("Add Comment Failed", e);
243 }
244 break;
245 }
246 }
247 }
248 });
249
250 HorizontalPanel commentSectionPanel = new HorizontalPanel();
251 commentSectionPanel.add(loggedInLabelsPanel);
252 commentSectionPanel.add(commentEditPanel);
253 commentSectionPanel.add(notAuthorizedToAddComments);
254 contentPanel.add(commentSectionPanel);
255
256
257 contentPanel.add(commentsTableLayout);
258
259
260
261
262 commentLightBox.setWidget(contentPanel);
263 setEditMode(EditMode.ADD_COMMENT);
264 }
265
266 private void checkPermissionsAndRedrawTable(final List<CommentInfo> commentInfos) {
267
268 commentServiceAsync.isAuthorizedAddComment(referenceId, referenceTypeKey, new KSAsyncCallback<Boolean>() {
269
270 @Override
271 public void onFailure(Throwable caught) {
272 GWT.log("Error checking permission for adding comments: ", caught);
273 throw new RuntimeException("Error checking Permissions: ", caught);
274 }
275
276 @Override
277 public void onSuccess(Boolean result) {
278 GWT.log("User is " + ((result) ? "" : "not ") +
279 "authorized to add comment.", null);
280 if(referenceId != null && !(referenceId.isEmpty())){
281 notAuthorizedToAddComments.setVisible(false);
282 commentEditPanel.setVisible(result);
283 commentsTableLayout.setVisible(true);
284 redrawCommentsTable(commentInfos);
285 }
286 else{
287 notAuthorizedToAddComments.setVisible(true);
288 commentEditPanel.setVisible(false);
289 commentsTableLayout.setVisible(false);
290 }
291 }
292
293 });
294 }
295
296 private void redrawCommentsTable(List<CommentInfo> commentInfos) {
297 commentsTableLayout.clear();
298 editControlsCallbacks.clear();
299
300 if (commentInfos != null) {
301 int rowIndex = 0;
302 int commentCounter = 0;
303 for (final CommentInfo commentInfo : commentInfos) {
304 int columnIndex = 0;
305 if (commentInfo.getType() != null &&
306 commentInfo.getType().startsWith("kuali.comment.type.workflowDecisionRationale")) {
307
308 continue;
309 }
310 if (rowIndex == 0) {
311 StringBuilder titleTextSb = new StringBuilder();
312 titleTextSb.append("Comments (").append(commentInfos.size()).append(")");
313 SectionTitle commentsSectionHeader = SectionTitle.generateH3Title(titleTextSb.toString());
314 commentsSectionHeader.getElement().getStyle().setProperty("borderBottom", "1px solid #D8D8D8");
315 commentsTableLayout.setWidget(rowIndex, columnIndex, commentsSectionHeader);
316 commentsTableLayout.getFlexCellFormatter().setColSpan(rowIndex, columnIndex, 3);
317 rowIndex++;
318 }
319 if (commentCounter > 0) {
320 VerticalFlowPanel space = new VerticalFlowPanel();
321 space.getElement().getStyle().setPaddingBottom(5d, Style.Unit.PX);
322 commentsTableLayout.setWidget(rowIndex, columnIndex, space);
323 commentsTableLayout.getFlexCellFormatter().setColSpan(rowIndex, columnIndex, 3);
324 rowIndex++;
325 }
326 VerticalFlowPanel userNameAndTime = new VerticalFlowPanel();
327 final HTML userNameLabel = new HTML();
328
329 final String userId = commentInfo.getMetaInfo().getUpdateId();
330 commentServiceAsync.getUserRealName(userId, new AsyncCallback<String>() {
331 @Override
332 public void onFailure(Throwable caught) {
333 userNameLabel.setHTML("<b>" + userId + "</b>");
334 }
335 @Override
336 public void onSuccess(String result) {
337 if (result != null && !result.isEmpty()) {
338 userNameLabel.setHTML("<b>" + result + "</b>");
339 } else {
340 userNameLabel.setHTML("<b>" + userId + "</b>");
341 }
342 }
343 });
344 Date createTime = commentInfo.getMetaInfo().getCreateTime();
345 userNameAndTime.add(userNameLabel);
346 userNameAndTime.add(new KSLabel(df.format(createTime)));
347 userNameAndTime.getElement().getStyle().setPaddingRight(20d, Style.Unit.PX);
348 commentsTableLayout.setWidget(rowIndex, columnIndex, userNameAndTime);
349 columnIndex++;
350
351 RichTextInfo commentRT = commentInfo.getCommentText();
352 String commentText = commentRT.getPlain();
353 KSLabel commentTextLabel = new KSLabel(commentText);
354 commentTextLabel.getElement().getStyle().setPaddingRight(20d, Style.Unit.PX);
355 commentTextLabel.setWidth("120px");
356 commentTextLabel.getElement().getStyle().setProperty("wordWrap", "break-word");
357 commentsTableLayout.setWidget(rowIndex, columnIndex, commentTextLabel);
358 columnIndex++;
359
360 final KSButton editButton = new KSButton("Edit", ButtonStyle.DEFAULT_ANCHOR);
361 final KSButton deleteButton = new KSButton("Delete", ButtonStyle.DEFAULT_ANCHOR);
362 editButton.getElement().getStyle().setPadding(5d, Style.Unit.PX);
363 editControlsCallbacks.add(new Callback<EditMode>() {
364 @Override
365 public void exec(EditMode result) {
366 switch (editMode) {
367 case UPDATE_COMMENT:
368
369
370 editButton.setEnabled(false);
371 deleteButton.setEnabled(false);
372 break;
373 case ADD_COMMENT:
374
375
376 editButton.setEnabled(true);
377 deleteButton.setEnabled(true);
378 break;
379 }
380 }
381 });
382
383 editButton.addClickHandler(new ClickHandler() {
384 @Override
385 public void onClick(ClickEvent event) {
386 String commentText = (commentInfo == null ||
387 commentInfo.getCommentText() == null)? "" :
388 commentInfo.getCommentText().getPlain();
389 selectedComment = commentInfo;
390 commentTextArea.setText(commentText);
391 commentTextArea.setFocus(true);
392 setEditMode(EditMode.UPDATE_COMMENT);
393 }
394 });
395 deleteButton.addClickHandler(new ClickHandler() {
396 @Override
397 public void onClick(ClickEvent event) {
398 final ConfirmationDialog confirmDeletion =
399 new ConfirmationDialog("Delete Comment",
400 "You are about to delete a comment. Are you sure?");
401 confirmDeletion.getConfirmButton().addClickHandler(new ClickHandler(){
402 @Override
403 public void onClick(ClickEvent event) {
404 try {
405 commentServiceAsync.removeComment(commentInfo.getId(),
406 referenceId, referenceTypeKey,
407 new KSAsyncCallback<StatusInfo>(){
408
409 @Override
410 public void handleFailure(Throwable caught) {
411 GWT.log("remove Comment Failed", caught);
412 }
413
414 @Override
415 public void onSuccess(StatusInfo result) {
416 confirmDeletion.hide();
417 refreshComments();
418 }
419
420 });
421 } catch (Exception e) {
422 GWT.log("remove Comment Failed", e);
423 }
424 }
425 });
426 confirmDeletion.show();
427 }
428 });
429 commentsTableLayout.setWidget(rowIndex, columnIndex, editButton);
430 columnIndex++;
431 commentsTableLayout.setWidget(rowIndex, columnIndex, deleteButton);
432 columnIndex++;
433 if (userId == null || !userId.equals(this.loggedInUserId)) {
434 editButton.setVisible(false);
435 deleteButton.setVisible(false);
436 }
437
438 rowIndex++;
439
440 commentCounter++;
441 }
442 setEditMode(EditMode.ADD_COMMENT);
443 }
444 }
445
446 public void setEditMode(EditMode editMode) {
447 this.editMode = editMode;
448 switch (editMode) {
449 case UPDATE_COMMENT:
450 cancelEditButton.setVisible(true);
451 if (editControlsCallbacks != null) {
452 for (Callback<EditMode> callback : editControlsCallbacks) {
453 callback.exec(EditMode.UPDATE_COMMENT);
454 }
455 }
456 break;
457 case ADD_COMMENT:
458 cancelEditButton.setVisible(false);
459 commentTextArea.setText("");
460 if (editControlsCallbacks != null) {
461 for (Callback<EditMode> callback : editControlsCallbacks) {
462 callback.exec(EditMode.ADD_COMMENT);
463 }
464 }
465 break;
466 }
467 }
468
469 public void refreshComments(){
470
471 try {
472 commentServiceAsync.getComments(referenceId, referenceTypeKey, new KSAsyncCallback<List<CommentInfo>>(){
473
474 @Override
475 public void handleFailure(Throwable caught) {
476 GWT.log("getComments failed", caught);
477 }
478
479 @Override
480 public void onSuccess(List<CommentInfo> result) {
481 if (result != null && !result.isEmpty()) {
482 Collections.sort(result, new Comparator<CommentInfo>(){
483
484 @Override
485 public int compare(CommentInfo comment1, CommentInfo comment2) {
486
487 if(comment1.getMetaInfo().getCreateTime().after(comment2.getMetaInfo().getCreateTime())){
488 return -1;
489 }
490 else if(comment1.getMetaInfo().getCreateTime().before(comment2.getMetaInfo().getCreateTime())){
491 return 1;
492 }
493 else{
494
495 return 0;
496 }
497
498 }
499 });
500 }
501 checkPermissionsAndRedrawTable(result);
502 }
503 });
504 } catch (Exception e) {
505 Window.alert("Failed to refresh Comments");
506 GWT.log("refresh Comments Failed",e);
507 }
508
509
510 }
511
512
513
514
515
516
517
518
519
520 @Override
521 public String getReferenceId() {
522 return referenceId;
523 }
524
525 @Override
526 public String getReferenceState() {
527 return referenceState;
528 }
529
530 @Override
531 public String getReferenceType() {
532 return referenceType;
533 }
534
535 @Override
536 public String getReferenceTypeKey() {
537 return referenceTypeKey;
538 }
539
540 @Override
541 public void setReferenceId(String id) {
542 this.referenceId = id;
543 }
544
545 @Override
546 public void setReferenceState(String state) {
547 this.referenceState = state;
548 }
549
550 @Override
551 public void setReferenceType(String type) {
552 this.referenceType = type;
553 }
554
555 @Override
556 public void setReferenceTypeKey(String key) {
557 this.referenceTypeKey = key;
558 }
559
560 public void setReferenceAttributes(Map<String, String> referenceAttributes) {
561 this.referenceAttributes = referenceAttributes;
562 }
563
564 public void show() {
565 controller.requestModel(ReferenceModel.class,
566 new ModelRequestCallback<ReferenceModel>(){
567 public void onModelReady(ReferenceModel model) {
568 String proposalTitleString = null;
569 setReferenceId(model.getReferenceId());
570 setReferenceTypeKey(model.getReferenceTypeKey());
571 setReferenceType(model.getReferenceType());
572 setReferenceState(model.getReferenceState());
573 setReferenceAttributes(model.getReferenceAttributes());
574 proposalTitleString = (model.getReferenceAttributes() == null)? null :
575 model.getReferenceAttributes().get("name");
576 if (proposalTitleString != null && !proposalTitleString.trim().isEmpty()) {
577 proposalTitle.setText(proposalTitleString);
578 proposalTitle.setVisible(true);
579 } else {
580 proposalTitle.setText("");
581 proposalTitle.setVisible(false);
582 }
583 refreshComments();
584 commentLightBox.show();
585 }
586
587 public void onRequestFail(Throwable cause) {
588 Window.alert(cause.toString());
589 }
590 });
591 }
592
593 }