Skip to content

Commit 15a975b

Browse files
gautamdshethGautam Sheth
andauthored
Feature #3195: Refactor FilePipeBind and MailUtility to improve file handling and update documentation for email attachments (#4570)
Co-authored-by: Gautam Sheth <[email protected]>
1 parent e392e67 commit 15a975b

File tree

5 files changed

+125
-27
lines changed

5 files changed

+125
-27
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
3737
- Added `-CanSyncHubSitePermissions` parameter to `Set-PnPSite` cmdlet to set value of allowing syncing hub site permissions to this associated site.
3838
- Added `Get-PnPProfileCardProperty`, `New-PnPProfileCardProperty` and `Remove-PnPProfileCardProperty` cmdlets to manage showing additional properties on the Microsoft 365 user profile [#4548](https://github.com/pnp/powershell/pull/4548)
3939
- Added `-Contributors` and `-Managers` parameters to `New-PnPTermGroup` and `Set-PnPTermGroup` cmdlets.
40+
- Added `-Files` parameter for `Send-PnPMail` cmdlet to allow files to be downloaded from SharePoint and then sent as attachments.
4041

4142
### Changed
4243

documentation/Send-PnPMail.md

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@ Allows sending an e-mail
1414

1515
## SYNTAX
1616

17-
### Send through Microsoft Graph
17+
### Send through Microsoft Graph with attachments from local file system
1818

1919
```powershell
20-
Send-PnPMail -From <String> -To <String[]> -Subject <String> -Body <String> [-Cc <String[]>] [-Bcc <String[]>] [-ReplyTo <String[]>] [-Importance <MessageImportanceType>] [-BodyContentType <MessageBodyContentType>] [-SaveToSentItems <bool>] [-Connection <PnPConnection>] [-Verbose]
20+
Send-PnPMail -From <String> -To <String[]> -Subject <String> -Body <String> [-Cc <String[]>] [-Bcc <String[]>] [-ReplyTo <String[]>] [-Importance <MessageImportanceType>] [-BodyContentType <MessageBodyContentType>] [-SaveToSentItems <bool>] [-Connection <PnPConnection>] [-Verbose] [-Attachments <String[]>]
21+
```
22+
23+
### Send through Microsoft Graph with attachments from SPO
24+
25+
```powershell
26+
Send-PnPMail -From <String> -To <String[]> -Subject <String> -Body <String> [-Cc <String[]>] [-Bcc <String[]>] [-ReplyTo <String[]>] [-Importance <MessageImportanceType>] [-BodyContentType <MessageBodyContentType>] [-SaveToSentItems <bool>] [-Connection <PnPConnection>] [-Verbose] [-Files <String[]>]
2127
```
2228

2329
### Send through SharePoint Online (Default)
@@ -53,6 +59,20 @@ Send-PnPMail -To "[email protected]" -Subject "Test message" -B
5359

5460
Sends an e-mail using the SharePoint Online SendEmail method using the current context. E-mail is sent from the SharePoint Online no-reply e-mail address and can only be sent to accounts in the same tenant. The from address will show the title of the site you are connected with along with the e-mail address [email protected].
5561

62+
### EXAMPLE 4
63+
```powershell
64+
Send-PnPMail -From "[email protected]" -To "[email protected]" -Subject "Test message" -Body "This is a test message" -Attachments "C:\PnPCommunity\Test\test.docx"
65+
```
66+
67+
Sends an e-mail using Microsoft Graph to one recipient. E-mail is sent from the user specified in the From parameter and can be sent to both internal and external addresses. A copy of the sent e-mail will be stored in the mailbox of the user specified in the From parameter. It will also upload the file from the local file system as attachment.
68+
69+
### EXAMPLE 5
70+
```powershell
71+
Send-PnPMail -From "[email protected]" -To "[email protected]" -Subject "Test message" -Body "This is a test message" -Files "/sites/test/Shared Documents/Test.docx"
72+
```
73+
74+
Sends an e-mail using Microsoft Graph to one recipient. E-mail is sent from the user specified in the From parameter and can be sent to both internal and external addresses. A copy of the sent e-mail will be stored in the mailbox of the user specified in the From parameter. It will also upload the file from the SharePoint site collection and send it as attachment.
75+
5676
## PARAMETERS
5777

5878
### -Body
@@ -102,7 +122,7 @@ Allows defining what type of content is in the Body parameter. Defaults to HTML.
102122
103123
```yaml
104124
Type: MessageBodyContentType
105-
Parameter Sets: Send through Microsoft Graph
125+
Parameter Sets: Send through Microsoft Graph with attachments from SPO, Send through Microsoft Graph with attachments from local file system
106126
Accepted values: Html, Text
107127

108128
Required: False
@@ -131,7 +151,7 @@ The sender of the e-mail. When Microsoft Graph is used, this can be a user or a
131151
132152
```yaml
133153
Type: String
134-
Parameter Sets: Send through Microsoft Graph
154+
Parameter Sets: Send through Microsoft Graph with attachments from SPO, Send through Microsoft Graph with attachments from local file system
135155

136156
Required: True
137157
Position: Named
@@ -145,7 +165,7 @@ Allows defining what the importance of the e-mail is. Defaults to Normal.
145165
146166
```yaml
147167
Type: MessageImportanceType
148-
Parameter Sets: Send through Microsoft Graph
168+
Parameter Sets: Send through Microsoft Graph with attachments from SPO, Send through Microsoft Graph with attachments from local file system
149169
Accepted values: Low, Normal, High
150170

151171
Required: False
@@ -160,7 +180,7 @@ List of return addresses to use for the e-mail
160180
161181
```yaml
162182
Type: String[]
163-
Parameter Sets: Send through Microsoft Graph
183+
Parameter Sets: Send through Microsoft Graph with attachments from SPO, Send through Microsoft Graph with attachments from local file system
164184

165185
Required: False
166186
Position: Named
@@ -174,7 +194,7 @@ Allows indicating if the sent e-mail should be stored in the Sent Items of the m
174194
175195
```yaml
176196
Type: String[]
177-
Parameter Sets: Send through Microsoft Graph
197+
Parameter Sets: Send through Microsoft Graph with attachments from SPO, Send through Microsoft Graph with attachments from local file system
178198

179199
Required: False
180200
Position: Named
@@ -211,6 +231,34 @@ Accept pipeline input: False
211231
Accept wildcard characters: False
212232
```
213233
234+
### -Attachments
235+
List of attachments from local file system to be uploaded and sent as attachments.
236+
237+
```yaml
238+
Type: String[]
239+
Parameter Sets: Send through Microsoft Graph with attachments from local file system
240+
241+
Required: False
242+
Position: Named
243+
Default value: None
244+
Accept pipeline input: False
245+
Accept wildcard characters: False
246+
```
247+
248+
### -Files
249+
List of files from the SharePoint site collection to be sent as attachments.
250+
251+
```yaml
252+
Type: String[]
253+
Parameter Sets: Send through Microsoft Graph with attachments from SPO
254+
255+
Required: False
256+
Position: Named
257+
Default value: None
258+
Accept pipeline input: False
259+
Accept wildcard characters: False
260+
```
261+
214262
## RELATED LINKS
215263
216264
[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)

src/Commands/Base/PipeBinds/FilePipeBind.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using System;
2-
using Microsoft.SharePoint.Client;
3-
using System.Management.Automation;
1+
using Microsoft.SharePoint.Client;
42
using PnP.Core.Model.SharePoint;
53
using PnP.Core.Services;
4+
using System;
5+
using System.Management.Automation;
66

77
namespace PnP.PowerShell.Commands.Base.PipeBinds
88
{
@@ -62,7 +62,7 @@ public FilePipeBind(string id)
6262

6363
internal IFile GetCoreFile(PnPContext context, Cmdlet cmdlet = null)
6464
{
65-
if(CoreFile != null)
65+
if (CoreFile != null)
6666
{
6767
cmdlet?.WriteVerbose("File determined based on CoreFile instance");
6868
return CoreFile;
@@ -128,7 +128,7 @@ internal File GetFile(ClientContext context, Cmdlet cmdlet = null)
128128
if (!string.IsNullOrEmpty(ServerRelativeUrl))
129129
{
130130
cmdlet?.WriteVerbose("File will be retrieved based on server relative url");
131-
return context.Web.GetFileByServerRelativeUrl(ServerRelativeUrl);
131+
return context.Web.GetFileByServerRelativePath(ResourcePath.FromDecodedUrl(ServerRelativeUrl));
132132
}
133133

134134
throw new PSInvalidOperationException("No information available to retrieve file");

src/Commands/Utilities/MailUtility.cs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
using PnP.PowerShell.Commands.Base;
2-
using PnP.PowerShell.Commands.Utilities.REST;
3-
using System.Net.Http;
4-
using System.Text.Json;
5-
using System.Text.Json.Serialization;
6-
using System.Threading.Tasks;
7-
using PnP.PowerShell.Commands.Model.Mail;
8-
using Microsoft.SharePoint.Client;
9-
using System.Collections.Generic;
1+
using Microsoft.SharePoint.Client;
102
using Microsoft.SharePoint.Client.Utilities;
11-
using System.Net.Mail;
12-
using System.Net;
3+
using PnP.Core.Services;
4+
using PnP.PowerShell.Commands.Base;
5+
using PnP.PowerShell.Commands.Base.PipeBinds;
136
using PnP.PowerShell.Commands.Enums;
14-
using System.IO;
7+
using PnP.PowerShell.Commands.Model.Mail;
8+
using PnP.PowerShell.Commands.Utilities.REST;
159
using System;
10+
using System.Collections.Generic;
11+
using System.IO;
1612
using System.Management.Automation;
13+
using System.Net;
14+
using System.Net.Http;
15+
using System.Net.Mail;
16+
using System.Text.Json;
17+
using System.Text.Json.Serialization;
1718

1819
namespace PnP.PowerShell.Commands.Utilities
1920
{
@@ -192,5 +193,27 @@ public static List<MessageAttachmentOptions> GetListOfAttachments(string[] attac
192193

193194
return messageAttachmentOptions;
194195
}
196+
197+
public static List<MessageAttachmentOptions> GetListOfFiles(FilePipeBind[] files, PnPContext context)
198+
{
199+
if (files == null || files?.Length == 0)
200+
{
201+
return null;
202+
}
203+
List<MessageAttachmentOptions> messageAttachmentOptions = new List<MessageAttachmentOptions>();
204+
foreach (var file in files)
205+
{
206+
MessageAttachmentOptions item = new MessageAttachmentOptions();
207+
var attachmentFile = file.GetCoreFile(context);
208+
item.Type = "#microsoft.graph.fileAttachment";
209+
item.Name = attachmentFile.Name;
210+
var fileByteArray = attachmentFile.GetContentBytesAsync().GetAwaiter().GetResult();
211+
item.ContentBytes = Convert.ToBase64String(fileByteArray);
212+
MimeTypeMap.TryGetMimeType(attachmentFile.Name, out var mimeType);
213+
item.ContentType = mimeType ?? "text/plain";
214+
messageAttachmentOptions.Add(item);
215+
}
216+
return messageAttachmentOptions;
217+
}
195218
}
196219
}

src/Commands/Utilities/SendMail.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using PnP.PowerShell.Commands.Enums;
1+
using PnP.PowerShell.Commands.Base.PipeBinds;
2+
using PnP.PowerShell.Commands.Enums;
23
using PnP.PowerShell.Commands.Model.Mail;
4+
using System.Collections.Generic;
35
using System.Linq;
46
using System.Management.Automation;
57

@@ -8,47 +10,61 @@ namespace PnP.PowerShell.Commands.Utilities
810
[Cmdlet(VerbsCommunications.Send, "PnPMail", DefaultParameterSetName = ParameterSet_SENDTHROUGHSPO)]
911
public class SendMail : PnPWebCmdlet
1012
{
11-
private const string ParameterSet_SENDTHROUGHGRAPH = "Send through Microsoft Graph";
13+
private const string ParameterSet_SENDTHROUGHGRAPH = "Send through Microsoft Graph with attachments from local file system";
1214
private const string ParameterSet_SENDTHROUGHSPO = "Send through SharePoint Online";
15+
private const string ParameterSet_SENDTHROUGHGRAPHWITHFILES = "Send through Microsoft Graph with attachments from SPO";
1316

1417
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
18+
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
1519
public string From;
1620

1721
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
1822
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHSPO)]
23+
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
1924
public string[] To;
2025

2126
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
2227
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHSPO)]
28+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
2329
public string[] Cc;
2430

2531
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
2632
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHSPO)]
33+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
2734
public string[] Bcc;
2835

2936
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
3037
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHSPO)]
38+
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
3139
public string Subject;
3240

3341
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
3442
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHSPO)]
43+
[Parameter(Mandatory = true, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
3544
public string Body;
3645

3746
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
47+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
3848
public MessageImportanceType Importance;
3949

4050
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
51+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
4152
public string[] ReplyTo;
4253

4354
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
55+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
4456
public bool? SaveToSentItems;
4557

4658
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
59+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
4760
public MessageBodyContentType? BodyContentType;
4861

4962
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPH)]
5063
public string[] Attachments;
5164

65+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SENDTHROUGHGRAPHWITHFILES)]
66+
public FilePipeBind[] Files;
67+
5268
protected override void ExecuteCmdlet()
5369
{
5470
if (string.IsNullOrWhiteSpace(From))
@@ -60,6 +76,16 @@ protected override void ExecuteCmdlet()
6076
else
6177
{
6278
WriteVerbose($"Sending e-mail using Microsoft Graph");
79+
List<MessageAttachmentOptions> messageAttachmentOptions = null;
80+
if (ParameterSpecified(nameof(Attachments)))
81+
{
82+
messageAttachmentOptions = MailUtility.GetListOfAttachments(Attachments, SessionState.Path.CurrentFileSystemLocation.Path);
83+
}
84+
else if (ParameterSpecified(nameof(Files)))
85+
{
86+
messageAttachmentOptions = MailUtility.GetListOfFiles(Files, Connection.PnPContext);
87+
}
88+
6389
MailUtility.SendGraphMail(this, Connection, GraphAccessToken, new Message
6490
{
6591
Subject = Subject,
@@ -74,7 +100,7 @@ protected override void ExecuteCmdlet()
74100
Sender = new Recipient { EmailAddress = new EmailAddress { Address = From } },
75101
ReplyTo = ReplyTo?.Select(t => new Recipient { EmailAddress = new EmailAddress { Address = t } }).ToList(),
76102
Importance = Importance,
77-
Attachments = MailUtility.GetListOfAttachments(Attachments, SessionState.Path.CurrentFileSystemLocation.Path)
103+
Attachments = messageAttachmentOptions
78104
}, SaveToSentItems ?? true);
79105
}
80106

0 commit comments

Comments
 (0)