Browse code

FEATBL-829 Feat: attachment/event UI

Thomas Fricker authored on 27/08/2019 15:26:58
Showing 11 changed files
... ...
@@ -13,5 +13,6 @@ Require-Bundle: net.bluemind.core.commons;bundle-version="1.0.0",
13 13
  net.bluemind.lib.jackson;bundle-version="1.0.0",
14 14
  net.bluemind.slf4j;bundle-version="1.0.0",
15 15
  net.bluemind.core.task.api,
16
- net.bluemind.icalendar.api;bundle-version="1.0.0";visibility:=reexport
16
+ net.bluemind.icalendar.api;bundle-version="1.0.0";visibility:=reexport,
17
+ net.bluemind.attachment.api
17 18
 
... ...
@@ -21,6 +21,7 @@ package net.bluemind.calendar.api;
21 21
 import java.util.Collections;
22 22
 import java.util.stream.Collectors;
23 23
 
24
+import net.bluemind.attachment.api.AttachedFile;
24 25
 import net.bluemind.core.api.BMApi;
25 26
 import net.bluemind.core.api.date.BmDateTime;
26 27
 import net.bluemind.icalendar.api.ICalendarElement;
... ...
@@ -47,8 +48,7 @@ public class VEvent extends ICalendarElement {
47 48
 	 * </ul>
48 49
 	 * </p>
49 50
 	 * 
50
-	 * @return isAllday a boolean to specify if {@link VEvent} is en all day
51
-	 *         event
51
+	 * @return isAllday a boolean to specify if {@link VEvent} is en all day event
52 52
 	 */
53 53
 	public boolean allDay() {
54 54
 		return dtstart != null && dtstart.precision.equals(BmDateTime.Precision.Date);
... ...
@@ -91,6 +91,14 @@ public class VEvent extends ICalendarElement {
91 91
 		if (null != this.dtend) {
92 92
 			copy.dtend = new BmDateTime(this.dtend.iso8601, this.dtend.timezone, this.dtend.precision);
93 93
 		}
94
+		copy.attachments = this.attachments.stream().map(att -> {
95
+			AttachedFile file = new AttachedFile();
96
+			file.name = att.name;
97
+			file.expirationDate = att.expirationDate;
98
+			file.publicUrl = att.publicUrl;
99
+			return file;
100
+		}).collect(Collectors.toList());
101
+
94 102
 		copy.transparency = transparency;
95 103
 
96 104
 		return copy;
... ...
@@ -46,6 +46,7 @@ public class VEventOccurrence extends VEvent {
46 46
 		occurrence.transparency = evt.transparency;
47 47
 		occurrence.recurid = recurid;
48 48
 		occurrence.rrule = evt.rrule;
49
+		occurrence.attachments = evt.attachments;
49 50
 		return occurrence;
50 51
 	}
51 52
 
... ...
@@ -37,7 +37,6 @@ import java.util.stream.Collectors;
37 37
 
38 38
 import org.apache.commons.lang.StringUtils;
39 39
 
40
-import net.bluemind.attachment.api.AttachedFile;
41 40
 import net.bluemind.calendar.api.VEvent;
42 41
 import net.bluemind.calendar.api.VEvent.Transparency;
43 42
 import net.bluemind.calendar.api.VEventOccurrence;
... ...
@@ -402,13 +401,6 @@ public class VEventServiceHelper extends ICal4jEventHelper<VEvent> {
402 401
 		appendXMsProperties(properties, vevent);
403 402
 		appendXMozProperties(properties);
404 403
 
405
-		if (vevent.attachments != null) {
406
-			for (AttachedFile file : vevent.attachments) {
407
-				XProperty prop = new XProperty("X-BM-ATTACHMENT", "(" + file.name + ")" + file.publicUrl);
408
-				properties.add(prop);
409
-			}
410
-		}
411
-
412 404
 		return ret;
413 405
 	}
414 406
 
... ...
@@ -19,11 +19,8 @@
19 19
 package net.bluemind.imip.parser.impl;
20 20
 
21 21
 import java.io.IOException;
22
-import java.util.ArrayList;
23 22
 import java.util.LinkedList;
24 23
 import java.util.List;
25
-import java.util.regex.Matcher;
26
-import java.util.regex.Pattern;
27 24
 
28 25
 import org.apache.james.mime4j.dom.BinaryBody;
29 26
 import org.apache.james.mime4j.dom.Body;
... ...
@@ -33,11 +30,9 @@ import org.apache.james.mime4j.dom.Message;
33 30
 import org.apache.james.mime4j.dom.Multipart;
34 31
 import org.apache.james.mime4j.dom.field.ContentTypeField;
35 32
 import org.apache.james.mime4j.message.BodyPart;
36
-import org.apache.james.mime4j.stream.Field;
37 33
 import org.slf4j.Logger;
38 34
 import org.slf4j.LoggerFactory;
39 35
 
40
-import net.bluemind.attachment.api.AttachedFile;
41 36
 import net.bluemind.imip.parser.IIMIPParser;
42 37
 import net.bluemind.imip.parser.IMIPInfos;
43 38
 import net.bluemind.imip.parser.ITIPMethod;
... ...
@@ -73,25 +68,6 @@ public class IMIPParserImpl implements IIMIPParser {
73 68
 		List<Entity> parts = mp.getBodyParts();
74 69
 		parts = expandParts(parts);
75 70
 
76
-		List<Field> attachments = m.getHeader().getFields("X-BM-ATTACHMENT");
77
-		List<AttachedFile> attachedFiles = new ArrayList<>();
78
-		if (attachments != null) {
79
-			String headerPattern = "\\((.*)\\)(.*)";
80
-			Pattern compile = Pattern.compile(headerPattern);
81
-			for (Field field : attachments) {
82
-				Matcher matcher = compile.matcher(field.getBody());
83
-				if (matcher.find()) {
84
-					String name = matcher.group(1);
85
-					String uri = matcher.group(2);
86
-					AttachedFile file = new AttachedFile();
87
-					file.name = name;
88
-					file.publicUrl = uri;
89
-					attachedFiles.add(file);
90
-				}
91
-			}
92
-
93
-		}
94
-
95 71
 		for (Entity e : parts) {
96 72
 			String mime = e.getMimeType();
97 73
 			if (!TEXT_CALENDAR.equals(mime) && !MS_TNEF.equals(mime)) {
... ...
@@ -123,6 +123,8 @@ net.bluemind.calendar.vevent.VEventAdaptor.prototype.toModelView = function(veve
123 123
     var MSG_PRIVATE = goog.getMsg('Private');
124 124
     model.summary = MSG_PRIVATE;
125 125
   }
126
+  
127
+  model.attachments = this.parseAttachments_(vevent);
126 128
 
127 129
   return model;
128 130
 };
... ...
@@ -285,6 +287,25 @@ net.bluemind.calendar.vevent.VEventAdaptor.prototype.parseRRule_ = function(veve
285 287
   return rrule;
286 288
 };
287 289
 
290
+net.bluemind.calendar.vevent.VEventAdaptor.prototype.parseAttachments_ = function(vevent) {
291
+  if (!vevent['attachments']) {
292
+    return [];
293
+  }
294
+  
295
+  var attachments = goog.array.map(vevent['attachments'], function(attachment) {
296
+    return {
297
+      publicUrl : attachment['publicUrl'],
298
+      name : attachment['name']
299
+    };
300
+  }, this);
301
+  
302
+  for (var i = 0; i < attachments.length; i++) { 
303
+    attachments[i].index = i;
304
+  } 
305
+  
306
+  return attachments;
307
+}
308
+
288 309
 /**
289 310
  * Test if the given item container is owned by a given attendee
290 311
  * 
... ...
@@ -354,6 +375,15 @@ net.bluemind.calendar.vevent.VEventAdaptor.prototype.fromModelView = function(mo
354 375
       }
355 376
     });
356 377
   }
378
+  
379
+  if (goog.isArray(model.attachments)) {
380
+    vevent['attachments'] = goog.array.map(model.attachments, function(attachment) {
381
+      return {
382
+        'publicUrl' : attachment.publicUrl,
383
+        'name' : attachment.name
384
+      }
385
+    });
386
+  }
357 387
 
358 388
   if (goog.isArray(model.attendees) && model.attendees.length > 0) {
359 389
     vevent['attendees'] =
... ...
@@ -195,6 +195,27 @@
195 195
                 </ul>
196 196
               </div>
197 197
             </div>
198
+            <div class="{css bm-ui-form-entry} {css bm-clearfix}">
199
+              <label id="attachment-label" for="bm-ui-form-owner">{msg meaning="calendar.event.attachments" desc="Attachments"}Attachments{/msg}</label>
200
+              <div class="{css bm-ui-form-input}">
201
+                
202
+                <div id="bm-attachment-list">
203
+                	{foreach $attachment in $event.attachments}
204
+                  		{call .attachmentEntry}
205
+                  			{param attachment : $attachment /}
206
+                  		{/call}
207
+                  	{/foreach}	
208
+                </div>  
209
+                  
210
+                <br>
211
+                <div class="{css bm-ui-form-inline}" id="bm-ui-form-no-attachment-block">
212
+                  <label id="attachment-label-local" for="bm-ui-form-owner">{msg meaning="calendar.event.addAttachmentLocal" desc="Button to add an attachment"}Add{/msg}</label>
213
+                  <input type="file" id="localAttachmentFile">&nbsp;&nbsp;<br>
214
+                  <p><div class="goog-inline-block goog-link-button" role="button" id="bm-ui-form-add-attachment-server" style="-webkit-user-select: none;" tabindex="0">{msg meaning="calendar.event.addAttachmentServer" desc="Button to add an attachment"}Add from Server{/msg}</div></p>
215
+                </div>
216
+                
217
+              </div>
218
+            </div>
198 219
           </fieldset>
199 220
           <fieldset id="bm-ui-form-fieldset-repeat" class="{css goog-tab-content}" style='display: none'>
200 221
           <div class="{css bm-ui-form-entry} {css bm-clearfix}">
... ...
@@ -667,3 +688,14 @@
667 688
   {/if}
668 689
 {/template}
669 690
 
691
+{template .attachmentEntry}
692
+	<div style="height: 20px;" id="div-bm-ui-form-delete-attachment-{$attachment.index}">
693
+	    <a href="{$attachment.publicUrl}" target="_blank"><i class="fa fa-paperclip"></i>&nbsp;{$attachment.name}</a>&nbsp;&nbsp;
694
+		    <div class="{css bm-ui-form-inline}" id="bm-ui-form-no-reminder-block">
695
+		    <div class="goog-inline-block"  role="button" id="bm-ui-form-delete-attachment-{$attachment.index}" style="-webkit-user-select: none; cursor: pointer" tabindex="0"><i class="fa fa-trash fa-lg" aria-hidden="true"></i></div>
696
+	    </div>
697
+    <br>
698
+</div>
699
+{/template}
700
+
701
+
... ...
@@ -463,6 +463,65 @@ net.bluemind.calendar.vevent.ui.Form.prototype.enterDocument = function() {
463 463
     });
464 464
   });
465 465
   el = dom.getElement('bm-ui-form-reminder');
466
+  
467
+  // ATTACHMENTS
468
+  goog.array.map(this.getModel().attachments, function(attachment) {
469
+    handler.listen(dom.getElement('bm-ui-form-delete-attachment-'+attachment.index), goog.events.EventType.CLICK, this.delAttachment(attachment));
470
+  }, this);
471
+  
472
+  var canRemoteAttach = goog.global['bmcSessionInfos']['roles'].split(',').includes('canRemoteAttach');
473
+  if (!canRemoteAttach){
474
+    this.getDomHelper().removeNode(dom.getElement('bm-ui-form-no-attachment-block'));
475
+    if (this.getModel().attachments.length == 0){
476
+      this.getDomHelper().removeNode(dom.getElement('attachment-label'));
477
+    }
478
+  } else {
479
+    handler.listen(dom.getElement('localAttachmentFile'), goog.events.EventType.CHANGE, function() {
480
+      var fileInput = document.getElementById('localAttachmentFile');
481
+      var file = fileInput.files[0];
482
+      var formData = new FormData();
483
+      formData.append('file', file);
484
+      
485
+      var sid = goog.global['bmcSessionInfos']['sid'];
486
+      var domain = goog.global['bmcSessionInfos']['domain'];
487
+      var url = '/api/attachment/' + encodeURIComponent(domain) + '/' + encodeURIComponent(file.name) + '/share';
488
+      var xhr = new XMLHttpRequest();
489
+      xhr.open('PUT', url, true);
490
+      xhr.setRequestHeader('X-BM-ApiKey', sid);
491
+      var that = this;  
492
+      xhr.onload = function () {
493
+          var ret = JSON.parse(this.response);
494
+          var index = 0;
495
+          for (i = 0; i < that.getModel().attachments.length; i++) { 
496
+            index = Math.max(that.getModel().attachments[i].index, index);
497
+          } 
498
+          index++;
499
+          var publicUrl = ret['publicUrl'];
500
+          var name = ret['name'];
501
+          var newAttachment  = {
502
+              publicUrl : publicUrl,
503
+              name : name,
504
+              index : index
505
+          }
506
+          
507
+          that.getModel().attachments.push(newAttachment);
508
+          var entry = soy.renderAsFragment(net.bluemind.calendar.vevent.templates.attachmentEntry, {
509
+            attachment : newAttachment
510
+          });
511
+
512
+          that.getDomHelper().appendChild(dom.getElement('bm-attachment-list'), entry);
513
+          handler.listen(dom.getElement('bm-ui-form-delete-attachment-'+newAttachment.index), goog.events.EventType.CLICK, that.delAttachment(newAttachment));
514
+          
515
+          dom.getElement('localAttachmentFile').value = "";
516
+      };
517
+      xhr.send(formData);
518
+      
519
+    });
520
+   
521
+    handler.listen(dom.getElement('bm-ui-form-add-attachment-server'), goog.events.EventType.CLICK, function() {
522
+      
523
+    });
524
+  }
466 525
 
467 526
   // LOCATION
468 527
   el = dom.getElement('bm-ui-form-location');
... ...
@@ -560,6 +619,20 @@ net.bluemind.calendar.vevent.ui.Form.prototype.enterDocument = function() {
560 619
 /**
561 620
  * @private
562 621
  */
622
+net.bluemind.calendar.vevent.ui.Form.prototype.delAttachment = function(attachment){
623
+  return function() {
624
+    for( var i = 0; i < this.getModel().attachments.length; i++){ 
625
+      if (this.getModel().attachments[i].name === attachment.name) {
626
+        this.getModel().attachments.splice(i, 1); 
627
+      }
628
+    }
629
+    this.getDomHelper().removeNode(this.getDomHelper().getElement('div-bm-ui-form-delete-attachment-'+attachment.index));
630
+   }
631
+}
632
+
633
+/**
634
+ * @private
635
+ */
563 636
 net.bluemind.calendar.vevent.ui.Form.prototype.setDTStart = function(date) {
564 637
   this.getChild('dstart').setDate(date);
565 638
   if (!this.getModel().states.allday) {
... ...
@@ -2273,12 +2346,18 @@ net.bluemind.calendar.vevent.ui.Form.prototype.addAttendee_ = function(attendee)
2273 2346
   }
2274 2347
 };
2275 2348
 
2276
-/** Request the computing of a resource template if any and add it to the event description if not already done. */
2349
+/**
2350
+ * Request the computing of a resource template if any and add it to the event
2351
+ * description if not already done.
2352
+ */
2277 2353
 net.bluemind.calendar.vevent.ui.Form.prototype.addResourceTemplateToDescription = function (attendee) {
2278 2354
   this.addOrRemoveResourceTemplateFromDescription(attendee, "add");
2279 2355
 }
2280 2356
 
2281
-/** Request the removing of a resource template from the event description if present. */
2357
+/**
2358
+ * Request the removing of a resource template from the event description if
2359
+ * present.
2360
+ */
2282 2361
 net.bluemind.calendar.vevent.ui.Form.prototype.removeResourceTemplateFromDescription = function (attendee) {
2283 2362
   this.addOrRemoveResourceTemplateFromDescription(attendee, "remove");
2284 2363
 }
... ...
@@ -19,6 +19,7 @@ calendar.event.occupation.available=Verf\u00FCgbar
19 19
 calendar.event.occupation.busy=Besch\u00E4ftigt
20 20
 calendar.event.occupation.label=Mich anzeigen als
21 21
 calendar.event.organizer=Organisator
22
+calendar.event.attachments=Anhänge
22 23
 calendar.event.participation=Teilnahme
23 24
 calendar.event.participation.addNote=Der Antwort vor dem Versand eine Notiz hinzuf\u00FCgen.
24 25
 calendar.event.participation.maybe=Vielleicht
... ...
@@ -108,6 +109,8 @@ general.yes=Ja
108 109
 search.results.prior=Zeige Resultate vor dem 
109 110
 search.results.after=Zeige Resultate nach dem
110 111
 calendar.addCalendar=Kalender hinzuf\u00FCgen...
112
+calendar.event.addAttachmentLocal=Lokalen Anhang hinzuf\u00FCgen
113
+calendar.event.addAttachmentServer=Anhang hinzuf\u00FCgen (Server)
111 114
 calendar.event.addAttendee=Teilnehmer hinzuf\u00FCgen...
112 115
 calendar.event.position.dayOfMonth.first=am ersten {$day} im {$month} 
113 116
 calendar.event.position.dayOfMonth.fourth=am vierten {$day} im {$month} 
... ...
@@ -5,6 +5,8 @@ calendar.back=Back to calendar
5 5
 calendar.calendar=Calendar
6 6
 calendar.event.addAttendee=Add an attendee or a resource...
7 7
 # or ['Add an attendee ...']
8
+calendar.event.addAttachmentLocal=Add local attachment
9
+calendar.event.addAttachmentServer=Add an attachment (Server)
8 10
 calendar.event.addReminder=Add a reminder
9 11
 calendar.event.allDay=All day
10 12
 calendar.event.attendees=Attendees
... ...
@@ -23,6 +25,7 @@ calendar.event.occupation.available=Available
23 25
 calendar.event.occupation.busy=Busy
24 26
 calendar.event.occupation.label=Show me as
25 27
 calendar.event.organizer=Organizer
28
+calendar.event.attachments=Attachments
26 29
 calendar.event.participation=Participation
27 30
 calendar.event.participation.addNote=Add a note to the response before sending
28 31
 calendar.event.participation.maybe=Maybe
... ...
@@ -19,7 +19,10 @@ calendar.event.occupation.available=Disponible
19 19
 calendar.event.occupation.busy=Occup\u00E9
20 20
 calendar.event.occupation.label=Disponibilit\u00E9
21 21
 calendar.event.organizer=Organisateur
22
+calendar.event.attachments=Pièces jointes 
22 23
 calendar.event.participation=Participation
24
+calendar.event.addAttachmentLocal=Ajouter une pièces jointe depuis  l'ordinateur
25
+calendar.event.addAttachmentServer=Ajouter une pièce jointes depuis le serveur
23 26
 calendar.event.participation.addNote=Ajouter une note \u00E0 la r\u00E9ponse avant de l’envoyer
24 27
 calendar.event.participation.maybe=Peut-\u00EAtre
25 28
 calendar.event.participation.notSendResponse=Ne pas envoyer de r\u00E9ponse