/
GdprService.cs
373 lines (318 loc) · 14 KB
/
GdprService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
using Nop.Core;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Gdpr;
using Nop.Core.Events;
using Nop.Data;
using Nop.Services.Authentication.External;
using Nop.Services.Blogs;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Forums;
using Nop.Services.Messages;
using Nop.Services.News;
using Nop.Services.Orders;
using Nop.Services.Stores;
namespace Nop.Services.Gdpr;
/// <summary>
/// Represents the GDPR service
/// </summary>
public partial class GdprService : IGdprService
{
#region Fields
protected readonly IAddressService _addressService;
protected readonly IBackInStockSubscriptionService _backInStockSubscriptionService;
protected readonly IBlogService _blogService;
protected readonly ICustomerService _customerService;
protected readonly IExternalAuthenticationService _externalAuthenticationService;
protected readonly IEventPublisher _eventPublisher;
protected readonly IForumService _forumService;
protected readonly IGenericAttributeService _genericAttributeService;
protected readonly INewsLetterSubscriptionService _newsLetterSubscriptionService;
protected readonly INewsService _newsService;
protected readonly IProductService _productService;
protected readonly IRepository<GdprConsent> _gdprConsentRepository;
protected readonly IRepository<GdprLog> _gdprLogRepository;
protected readonly IShoppingCartService _shoppingCartService;
protected readonly IStoreService _storeService;
#endregion
#region Ctor
public GdprService(IAddressService addressService,
IBackInStockSubscriptionService backInStockSubscriptionService,
IBlogService blogService,
ICustomerService customerService,
IExternalAuthenticationService externalAuthenticationService,
IEventPublisher eventPublisher,
IForumService forumService,
IGenericAttributeService genericAttributeService,
INewsService newsService,
INewsLetterSubscriptionService newsLetterSubscriptionService,
IProductService productService,
IRepository<GdprConsent> gdprConsentRepository,
IRepository<GdprLog> gdprLogRepository,
IShoppingCartService shoppingCartService,
IStoreService storeService)
{
_addressService = addressService;
_backInStockSubscriptionService = backInStockSubscriptionService;
_blogService = blogService;
_customerService = customerService;
_externalAuthenticationService = externalAuthenticationService;
_eventPublisher = eventPublisher;
_forumService = forumService;
_genericAttributeService = genericAttributeService;
_newsService = newsService;
_newsLetterSubscriptionService = newsLetterSubscriptionService;
_productService = productService;
_gdprConsentRepository = gdprConsentRepository;
_gdprLogRepository = gdprLogRepository;
_shoppingCartService = shoppingCartService;
_storeService = storeService;
}
#endregion
#region Utilities
/// <summary>
/// Insert a GDPR log
/// </summary>
/// <param name="gdprLog">GDPR log</param>
/// <returns>A task that represents the asynchronous operation</returns>
protected virtual async Task InsertLogAsync(GdprLog gdprLog)
{
await _gdprLogRepository.InsertAsync(gdprLog);
}
#endregion
#region Methods
#region GDPR consent
/// <summary>
/// Get a GDPR consent
/// </summary>
/// <param name="gdprConsentId">The GDPR consent identifier</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the gDPR consent
/// </returns>
public virtual async Task<GdprConsent> GetConsentByIdAsync(int gdprConsentId)
{
return await _gdprConsentRepository.GetByIdAsync(gdprConsentId, cache => default);
}
/// <summary>
/// Get all GDPR consents
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the gDPR consent
/// </returns>
public virtual async Task<IList<GdprConsent>> GetAllConsentsAsync()
{
var gdprConsents = await _gdprConsentRepository.GetAllAsync(query =>
{
return from c in query
orderby c.DisplayOrder, c.Id
select c;
}, cache => default);
return gdprConsents;
}
/// <summary>
/// Insert a GDPR consent
/// </summary>
/// <param name="gdprConsent">GDPR consent</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task InsertConsentAsync(GdprConsent gdprConsent)
{
await _gdprConsentRepository.InsertAsync(gdprConsent);
}
/// <summary>
/// Update the GDPR consent
/// </summary>
/// <param name="gdprConsent">GDPR consent</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task UpdateConsentAsync(GdprConsent gdprConsent)
{
await _gdprConsentRepository.UpdateAsync(gdprConsent);
}
/// <summary>
/// Delete a GDPR consent
/// </summary>
/// <param name="gdprConsent">GDPR consent</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task DeleteConsentAsync(GdprConsent gdprConsent)
{
await _gdprConsentRepository.DeleteAsync(gdprConsent);
}
/// <summary>
/// Gets the latest selected value (a consent is accepted or not by a customer)
/// </summary>
/// <param name="consentId">Consent identifier</param>
/// <param name="customerId">Customer identifier</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the result; null if previous a customer hasn't been asked
/// </returns>
public virtual async Task<bool?> IsConsentAcceptedAsync(int consentId, int customerId)
{
//get latest record
var log = (await GetAllLogAsync(customerId: customerId, consentId: consentId, pageIndex: 0, pageSize: 1)).FirstOrDefault();
if (log == null)
return null;
return log.RequestType switch
{
GdprRequestType.ConsentAgree => true,
GdprRequestType.ConsentDisagree => false,
_ => null,
};
}
#endregion
#region GDPR log
/// <summary>
/// Get all GDPR log records
/// </summary>
/// <param name="customerId">Customer identifier</param>
/// <param name="consentId">Consent identifier</param>
/// <param name="customerInfo">Customer info (Exact match)</param>
/// <param name="requestType">GDPR request type</param>
/// <param name="pageIndex">Page index</param>
/// <param name="pageSize">Page size</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the gDPR log records
/// </returns>
public virtual async Task<IPagedList<GdprLog>> GetAllLogAsync(int customerId = 0, int consentId = 0,
string customerInfo = "", GdprRequestType? requestType = null,
int pageIndex = 0, int pageSize = int.MaxValue)
{
return await _gdprLogRepository.GetAllPagedAsync(query =>
{
if (customerId > 0)
query = query.Where(log => log.CustomerId == customerId);
if (consentId > 0)
query = query.Where(log => log.ConsentId == consentId);
if (!string.IsNullOrEmpty(customerInfo))
query = query.Where(log => log.CustomerInfo == customerInfo);
if (requestType != null)
{
var requestTypeId = (int)requestType;
query = query.Where(log => log.RequestTypeId == requestTypeId);
}
query = query.OrderByDescending(log => log.CreatedOnUtc).ThenByDescending(log => log.Id);
return query;
}, pageIndex, pageSize);
}
/// <summary>
/// Insert a GDPR log
/// </summary>
/// <param name="customer">Customer</param>
/// <param name="consentId">Consent identifier</param>
/// <param name="requestType">Request type</param>
/// <param name="requestDetails">Request details</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task InsertLogAsync(Customer customer, int consentId, GdprRequestType requestType, string requestDetails)
{
ArgumentNullException.ThrowIfNull(customer);
var gdprLog = new GdprLog
{
CustomerId = customer.Id,
ConsentId = consentId,
CustomerInfo = customer.Email,
RequestType = requestType,
RequestDetails = requestDetails,
CreatedOnUtc = DateTime.UtcNow
};
await InsertLogAsync(gdprLog);
}
#endregion
#region Customer
/// <summary>
/// Permanent delete of customer
/// </summary>
/// <param name="customer">Customer</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task PermanentDeleteCustomerAsync(Customer customer)
{
ArgumentNullException.ThrowIfNull(customer);
//blog comments
var blogComments = await _blogService.GetAllCommentsAsync(customerId: customer.Id);
await _blogService.DeleteBlogCommentsAsync(blogComments);
//news comments
var newsComments = await _newsService.GetAllCommentsAsync(customerId: customer.Id);
await _newsService.DeleteNewsCommentsAsync(newsComments);
//back in stock subscriptions
var backInStockSubscriptions = await _backInStockSubscriptionService.GetAllSubscriptionsByCustomerIdAsync(customer.Id);
foreach (var backInStockSubscription in backInStockSubscriptions)
await _backInStockSubscriptionService.DeleteSubscriptionAsync(backInStockSubscription);
//product review
var productReviews = await _productService.GetAllProductReviewsAsync(customer.Id);
var reviewedProducts = await _productService.GetProductsByIdsAsync(productReviews.Select(p => p.ProductId).Distinct().ToArray());
await _productService.DeleteProductReviewsAsync(productReviews);
//update product totals
foreach (var product in reviewedProducts)
await _productService.UpdateProductReviewTotalsAsync(product);
//external authentication record
foreach (var ear in await _externalAuthenticationService.GetCustomerExternalAuthenticationRecordsAsync(customer))
await _externalAuthenticationService.DeleteExternalAuthenticationRecordAsync(ear);
//forum subscriptions
var forumSubscriptions = await _forumService.GetAllSubscriptionsAsync(customer.Id);
foreach (var forumSubscription in forumSubscriptions)
await _forumService.DeleteSubscriptionAsync(forumSubscription);
//shopping cart items
foreach (var sci in await _shoppingCartService.GetShoppingCartAsync(customer))
await _shoppingCartService.DeleteShoppingCartItemAsync(sci);
//private messages (sent)
foreach (var pm in await _forumService.GetAllPrivateMessagesAsync(0, customer.Id, 0, null, null, null, null))
await _forumService.DeletePrivateMessageAsync(pm);
//private messages (received)
foreach (var pm in await _forumService.GetAllPrivateMessagesAsync(0, 0, customer.Id, null, null, null, null))
await _forumService.DeletePrivateMessageAsync(pm);
//newsletter
var allStores = await _storeService.GetAllStoresAsync();
foreach (var store in allStores)
{
var newsletter = await _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreIdAsync(customer.Email, store.Id);
if (newsletter != null)
await _newsLetterSubscriptionService.DeleteNewsLetterSubscriptionAsync(newsletter);
}
//addresses
foreach (var address in await _customerService.GetAddressesByCustomerIdAsync(customer.Id))
{
await _customerService.RemoveCustomerAddressAsync(customer, address);
await _customerService.UpdateCustomerAsync(customer);
//now delete the address record
await _addressService.DeleteAddressAsync(address);
}
//generic attributes
var keyGroup = customer.GetType().Name;
var genericAttributes = await _genericAttributeService.GetAttributesForEntityAsync(customer.Id, keyGroup);
await _genericAttributeService.DeleteAttributesAsync(genericAttributes);
//ignore ActivityLog
//ignore ForumPost, ForumTopic, ignore ForumPostVote
//ignore Log
//ignore PollVotingRecord
//ignore ProductReviewHelpfulness
//ignore RecurringPayment
//ignore ReturnRequest
//ignore RewardPointsHistory
//and we do not delete orders
//remove from Registered role, add to Guest one
if (await _customerService.IsRegisteredAsync(customer))
{
var registeredRole = await _customerService.GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.RegisteredRoleName);
await _customerService.RemoveCustomerRoleMappingAsync(customer, registeredRole);
}
if (!await _customerService.IsGuestAsync(customer))
{
var guestRole = await _customerService.GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.GuestsRoleName);
await _customerService.AddCustomerRoleMappingAsync(new CustomerCustomerRoleMapping { CustomerId = customer.Id, CustomerRoleId = guestRole.Id });
}
var email = customer.Email;
//clear other information
customer.Email = string.Empty;
customer.EmailToRevalidate = string.Empty;
customer.Username = string.Empty;
customer.Active = false;
customer.Deleted = true;
await _customerService.UpdateCustomerAsync(customer);
//raise event
await _eventPublisher.PublishAsync(new CustomerPermanentlyDeleted(customer.Id, email));
}
#endregion
#endregion
}