I have an email provider that uses SMTP and IMAP from Mailkit. The main function I am using at the moment is to read messages from the inbox, and depending on the contents of the email they will go into a 'Processed' or an 'Ignored' folder. In existing systems we used with Exchange, these folders exist as subfolders to the Inbox. If the folders don't exist, they are created. A sample code snippet is below:
var _inboxFolder = _provider.GetInboxFolder();
var folder = _inboxFolder.GetChildFolder(folderName);
if (folder == null)
{
// If Gmail, Inbox does not allow children. Check for folder in root.
folder = _provider.GetFolder(folderName);
}
if (folder == null)
{
// Create new folder under Inbox
folder = _inboxFolder.CreateChildFolder(folderName);
if (folder == null)
{
// Cannot create under Inbox, create on root
folder = _provider.CreateFolderOnRoot(folderName);
}
}
These all use my own provider which is based on an interface we use for other providers, so there is some 'strange' logic there that makes sense for the other providers. GetInboxFolder and GetFolder returns a folder, but returns null if one can't be found. This works fine in all providers - if the folders don't exist, they can be created.
The issue arrives when creating the folders. If the folders cannot be found, they should be created under the Inbox folder. If this can't happen (like in Gmail), it should create them on the root folder. However, attempting to create a folder under the Inbox folder doesn't fail, it simply creates a label called "INBOX/FolderName". Ideally I would like to have any attempt to create this folder fail so it can simply be created on the root. I imagine this is an issue in the conversion from IMAP to the GMail label system.
Is there any way of preventing the "INBOX/FolderName" label, or identifying this issue before creating it? Or is there a way of identifying that this is a gmail server and implementing a special clause for it?
For a little extra info, the CreateChildFolder and CreateFolderOnRoot code is below.
IMAPFolder Class
public IEmailFolder CreateChildFolder(string displayName)
{
if (_imapClient == null || !_imapClient.IsConnected)
{
throw new NotLoggedInException();
}
return new ImapFolder(_folder.Create(displayName, true), _imapClient);
}
IMAPClient Class
public IEmailFolder CreateFolderOnRoot(string displayName)
{
if (_client == null)
{
throw new NotLoggedInException();
}
try
{
var toplevel = _client.GetFolder(_client.PersonalNamespaces[0]);
var mailkit = toplevel.Create(displayName, true);
return new ImapFolder(mailkit, _client);
}
catch (Exception exception)
{
throw new LoadFolderFailedException(exception);
}
}
The GMail "Label System" is how GMail implements folders. They do not implement physical folders, what they do is have "virtual" folders that are just whatever labels the user has defined and the folder is just a query of all mail that has that label applied.
That said, the GMail root namespace is string.Empty. Some IMAP servers have a root namespace of "INBOX" (such as Courier IMAP, I think).
Your CreateFolderOnRoot method should create a folder at the root for GMail, it won't create it under INBOX, so your question is very confusing.
If you want to add the folder under the INBOX, just use client.Inbox.Create (displayName, true);
The issue arrives when creating the folders. If the folders cannot be found, they should be created under the Inbox folder.
This makes me think that what you actually want to do is this:
try {
var toplevel = _client.GetFolder(_client.PersonalNamespaces[0]);
var mailkit = folder.GetSubfolder(displayName);
return new ImapFolder(mailkit, _client);
} catch (Exception ex) {
throw new LoadFolderFailedException(ex);
}
Notice the use of GetSubfolder as opposed to Create. Create will always create the folder. GetSubfolder will only get it if it exists.
Related
We're implementing a new workflow (combined with staging task sync) on an existing website where we would like to notify all members that "own" that particular section/content to approve changes.
One of the options is to have multiple roles and their corresponding workflows configured for their role and scope, but this seems like overkill - at least for us, as currently one single role is set for approvals (and another for editors)
However I've recently come across this new page property:
And have a couple of questions:
Can regular CMS users (without membership) be part of a group?
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
We have a Kentico 11 EMS license and working on an advanced workflow, therefore custom code is possible.
Can regular CMS users (without membership) be part of a group?
- why don't you use roles here?
Would we be able to leverage this group for the workflow's email
notifications instead of the roles? E.g. email to everyone in the
owner group when a page was sent for approval.
- you'll need to customize workflow manager class, but in general yes, it is possible. You could find an inspiration in this post
Is this option by default inherited from the parent page when a new
one is created or does it need to be set individually for each page?
- Use a macro to default the field. If you populate it with anything else then the new values will be saved.
Sample code snippet for Custom Global Event Handler for Workflow steps i.e., Reject and Approve steps.
using CMS;
using CMS.Base;
using CMS.DataEngine;
using CMS.DocumentEngine;
using CMS.EmailEngine;
using CMS.EventLog;
using CMS.Helpers;
using CMS.MacroEngine;
using CMS.SiteProvider;
using CMS.WorkflowEngine;
using System;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomWorkflowEvent))]
public class CustomWorkflowEvent : CMSModuleLoader
{
// Module class constructor, the system registers the module under the name "CustomInit"
public CustomWorkflowEvent()
: base("CustomInit")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit()
{
base.OnInit();
// Assigns custom handlers to events
// WorkflowEvents.Approve.After += WorkFlow_Event_After();
WorkflowEvents.Reject.After += WorkFlow_Event_After;
WorkflowEvents.Approve.After += Approve_After;
// WorkflowEvents.Action.After += WorkFlowAction_Event_After;
}
private void Approve_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
private void WorkFlow_Event_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
}
Hope Helps you.
Can regular CMS users (without membership) be part of a group?
It is not part of CMS users. Groups are coming from Groups Application.
GROUP: Allows you to manage user groups. Groups are a social networking
feature enabling users to find information and communicate according
to shared interests.
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
No
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
No
I've developed a project using asp.net core 2.0 + wiremock.net. Currently, I'm able to handle json files under "__admin/mappings" directory only. However, I have several json files and because of that, I'd like to add one more folder under the "mappings" directory, for example "__admin/mappings/{anotherFolder}".
What I have:
__admin/mappings/first.json
__admin/mappings/second.json
What I would like to have:
__admin/mappings/folder_A/first.json
__admin/mappings/folder_A/second.json
__admin/mappings/folder_B/first.json
__admin/mappings/folder_B/second.json
I've tried to add one more folder under the "mappings" folder, but when I tried to reach the json route, I got a message "No matching mapping found". Is there any way to handle json files from different directories?
I had to implement my own FileSystemHandler object and pass it on the FluentMockServerSettings constructor:
var stub = FluentMockServer.Start(
new FluentMockServerSettings
{
Urls = new[] {"http://+:5001" },
StartAdminInterface = true,
ReadStaticMappings = true,
WatchStaticMappings = true,
**FileSystemHandler = new CustomFileSystemFileHandler()**
}
);
You can use settings now
"WatchStaticMappingsInSubdirectories": true
Using code to Preserve access to a StorageFolder stolen from Karl Erickson
public static async Task<StorageFolder> GetOrPickAndRememberFolderAsync(string key)
{
if (StorageApplicationPermissions.FutureAccessList.ContainsItem(key))
{
return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(key);
}
var picker = new FolderPicker();
picker.FileTypeFilter.Add("*");
StorageFolder folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
StorageApplicationPermissions.FutureAccessList.AddOrReplace(key, folder);
}
return folder;
}
our UWP app is able to browse to and remember the location of a SQLite database chosen by a user. This should give read/write permissions on that folder.
The dbContext is instantiated like this
public dbContext(string dbPath)
{
databasePath = dbPath;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=" + databasePath);
}
This code works if the user chooses the default local storage location for the app.
var picker = new FolderPicker();
picker.FileTypeFilter.Add("*");
StorageFolder folder = await picker.PickSingleFolderAsync();
StorageFolder dbFolder = await GetOrPickAndRememberFolderAsync("LocalDbFolder");
string dbPath = Path.Combine(dbFolder.Path, "MySQLite.db");
using (var db = new dbContext(dbPath))
{
db.Database.Migrate();
}
However if the user chooses any other location (such as their Documents folder) the app fails with the error "SQLite Error 14: 'unable to open database file'."
Why would that be, given the user has given the app explicit permissions on the selected location through the picker?
You can't.
SQLite requires native file access. To use the native file access, you should put the sqlite .db file to the application local folders - Windows.Storage.ApplicationData.Current.LocalFolder or tempfolder.
If you use the file/folder picker, you can get the storagefolder item. But, if the folder is outside of your application, ALL of file accesses are done with OS's broker process. SQLite can't access the such of folders.
I am using the Domino Mail REST API and am able to create a new draft mail which appears in the Drafts folder.
When I update the draft mail it appears in the Sent folder and is no longer visible in the Drafts folder.
This is unexpected. The message was not sent. I have also tried setting the From and To fields to null and the sresult is always the same.
Partial code:
Gson gson = new Gson();
String json = gson.toJson(message);
// if message has an id then do update
if (href != null && href.trim().length() > 0) {
url = createFullQualifiedRequestUrl(href);
HttpPut request = new HttpPut(url);
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(json, "utf-8"));
response = this.executeRequest(request, username);
} else {
MailboxFolder folder = getFolder("drafts", username);
url = this.createFullQualifiedRequestUrl(folder.getLink()
.getHref());
HttpPost request = new HttpPost(url);
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(json, "utf-8"));
response = this.executeRequest(request, username);
}
if (response != null) {
SendMessageResult result = parseResponse(response);
if(href != null)
result.setLocation(href);
return result;
}
That's a bug in the REST mail API and Richard is correct about the root cause. The bug will be fixed in the next release of the extension library (901v00_12). I can't say exactly when release 12 will be available, but it should be soon.
Most likely, your problem is being caused by something that is setting the PostedDate item to a non-empty value.
The Sent "folder" is not a folder. It is a view. The same is true for the Drafts "folder". It is also a view. If you go into Domino Designer and look at the views, you can see their selection formula. You will see that they look something like this
Sent
SELECT DeliveredDate = "" & PostedDate != "" & !(#IsMember("S"; ExcludeFromView))
Drafts
SELECT PostedDate = "" & $MessageType = "" & #IsNotMember("D" : "A"; ExcludeFromView) & ISMAILSTATIONERY != 1 & Form != "Group" & Form != "Person"
Note that these are taken from a rather old version of the mail template, so what you actually see may be different, but AFAIK the idea has not changed. Documents appear in Sent if they contain a non-empty PostedDate item and the DeliveredDate item is either empty or missing and they are not marked for exclusion. Documents appear in Drafts if they do not contain either of those two date items, are not marked for exclusion, are not stationery, and are not Group or Person docs. The one thing that is in common here is the dependence on the PostedDate item.
How to move mail to a new folder in outlook?
My code:
using (ImapClient ic = new ImapClient(
imapAddr,
myEmailID,
myPasswd,
ImapClient.AuthMethods.Login,
portNo,
secureConn))
{
ic.SelectMailbox("INBOX");
bool headersOnly = false;
Lazy<MailMessage>[] messages = ic.SearchMessages(SearchCondition.Unseen(), headersOnly);
foreach (Lazy<MailMessage> message in messages)
{
MailMessage m = message.Value;
}
}
I try Google it but I can not find it .
Any suggestions are highly appreciated.
to move a message to another folder do this:
ic.MoveMessage(message.Uid, "some existing folder");
The uid is the unique identifiier for the mailmessage. I assume it maps to the message-id as described in RFC for Internet Message Format. Or otherwise to the UNIQUEID in the IMAP protocol.
to create a new folder use the this method:
ic.CreateMailbox("new mailbox name");
To send emails use an SmtpClient, like the one that is supplied in the .net framework:
using(SmtpClient client = new SmtpClient("your smtp server.com"))
{
client.Send("from#example.com",
"to#example.com",
"subject",
"Hello World");
}