0121 31 45 374
Qoute Icon

Group Newsletter Studio newsletters by month in the Umbraco backend tree

Tim

One of our customer's websites uses the Newsletter Studio Umbraco package to manage their mailing lists but had an issue because they send a lot of emails (40+ a month) so the draft/sent/archive folders were getting pretty large. We needed a way of grouping them more logically. Markus got back to me pretty quickly on the forums suggesting that we implement a custom tree controller and shared the original source controller code here.

The way we've implemented it is fairly simple -and not overly efficient due to the way the data access works but I thought it worth sharing as a couple of others have asked for the code, first though here's a little screenshot:

 

Before:
image

After:
image

 

Drafts are listed based on the created date, sent/archived generate folders based on the sent date as that felt most logical but it wouldn't be hard to change if you didn't like it. This was just a quick "get it out there ASAP" change so I'm sure the code can be cleared up. Welcome any comments.

 

Installation is simple, modify the `/config/trees.config` file:

 <add title="Newsletters" type="YourProject.NewsletterStudio.CustomNewsletterTreeController, YourProject" iconopen="icon-message" iconclosed="icon-message" application="NewsletterStudio" alias="Newsletter" sortorder="5" initialize="true"></add>

Then you just need to add this class to your solution:

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
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http.Formatting;
using NewsletterStudio.Core.Extensions;
using NewsletterStudio.Core.Interfaces.Data;
using NewsletterStudio.Core.Model;
using NewsletterStudio.Infrastucture;
using NewsletterStudio.Infrastucture.Services;
using NewsletterStudio.Umbraco;
using umbraco.BusinessLogic.Actions;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees;
 
namespace YourProject.NewsletterStudio
{
    [PluginController(NewsletterStudioApplication.Name)]
    [Tree("NewsletterStudio", "Newsletter", "Newsletters", "icon-message", "icon-message", sortOrder: 5)]
    public class CustomNewsletterTreeController : TreeController
    {
        private INewsletterRepository _newsletterRepository;
        private LocalizationService _localization;
 
        public CustomNewsletterTreeController()
        {
            _newsletterRepository = GlobalFactory.Current.NewsletterRepository;
            _localization = new LocalizationService();
        }
 
        protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
        {
            var node = base.CreateRootNode(queryStrings);
            node.Name = _localization.Get("ns_newsletters");
            return node;
        }
 
        protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
        {
            var nodes = new TreeNodeCollection();
 
            if (id == "-1")
            {
                var nodeDraft = this.CreateTreeNode("draft", id, queryStrings, _localization.Get("ns_draft"), "icon-outbox", _newsletterRepository.CountDraft() > 0);
                nodeDraft.RoutePath = "NewsletterStudio";
                nodes.Add(nodeDraft);
 
                var nodeSent = this.CreateTreeNode("sent", id, queryStrings, _localization.Get("ns_sent"), "icon-mailbox", _newsletterRepository.CountSent() > 0);
                nodeSent.RoutePath = "NewsletterStudio";
                nodes.Add(nodeSent);
 
                var nodeArchive = this.CreateTreeNode("archive", id, queryStrings, _localization.Get("ns_archive"), "icon-file-cabinet", _newsletterRepository.CountArchive() > 0);
                nodeArchive.RoutePath = "NewsletterStudio";
                nodes.Add(nodeArchive);
            }
            else
            {
                var statusParts = new[] { id };
                var emailStatus = id;
                if (emailStatus.Contains("-"))
                {
                    statusParts = emailStatus.Split('-');
                    emailStatus = statusParts.FirstOrDefault();
                }
 
                // NOTE: This is messy as we're getting all items just to get the years/months but we don't have much of a choice atm
                IList letterList;
                switch (emailStatus)
                {
                    case "draft":
                        letterList = _newsletterRepository.GetDraft();
                        break;
                    case "sent":
                        letterList = _newsletterRepository.GetSent();
                        break;
                    case "archive":
                        letterList = _newsletterRepository.GetArchived();
                        break;
                    default:
                        letterList = new List();
                        break;
                }
 
                // This is a year or month tree node
                if (statusParts.Length > 1)
                {
                    var subPart = statusParts[1];
                    var year = Convert.ToInt32(statusParts[2]);
 
                    var letterYears = letterList.Where(l => GetDateToGroupBy(emailStatus, l).Year == year);
                    switch (subPart)
                    {
                        case "year":
                            var months = letterYears.GroupBy(l => GetDateToGroupBy(emailStatus, l).Month);
                            foreach (var month in months.OrderBy(m => m.Key))
                            {
                                var item = CreateTreeNode($"{emailStatus}-month-{year}-{month.Key}", id, queryStrings, CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(month.Key), "icon-folder", true);
                                nodes.Add(item);
                            }
 
                            break;
                        case "month":
                            var monthNumber = Convert.ToInt32(statusParts[3]);
                            foreach (var newsletter in letterYears.Where(l => GetDateToGroupBy(emailStatus, l).Month == monthNumber).OrderBy(l => GetDateToGroupBy(emailStatus, l)))
                            {
                                var item = CreateTreeNode(newsletter.Id.ToString(), id, queryStrings, newsletter.Name, "icon-message", false);
 
                                if (newsletter.Status == NewsletterStatus.Completed)
                                {
                                    item.Icon = "icon-pie-chart";
                                    item.RoutePath = "NewsletterStudio/Newsletter/analytics/" + newsletter.Id;
                                }
                                else
                                {
                                    if (newsletter.ScheduledSendDate.HasValue)
                                        item.CssClasses.Add("ns-is-scheduled");
                                }
 
                                if (newsletter.Status == NewsletterStatus.Error)
                                    item.CssClasses.Add("ns-has-error");
 
                                nodes.Add(item);
                            }
 
                            break;
                    }
                }
                else
                {
                    var years = letterList.GroupBy(l => GetDateToGroupBy(emailStatus, l).Year);
                    foreach (var year in years.OrderBy(m => m.Key))
                    {
                        var item = this.CreateTreeNode($"{emailStatus}-year-{year.Key}", id, queryStrings, year.Key.ToString(), "icon-folder", true);
                        nodes.Add(item);
                    }
                }
            }
 
            return nodes;
        }
 
        private DateTime GetDateToGroupBy(string emailStatus, Newsletter newsletter)
        {
            if (emailStatus == "draft") return newsletter.CreatedDate;
 
            return newsletter.SentDate ?? newsletter.CreatedDate;
        }
 
        protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
        {
            var menu = new MenuItemCollection();
 
            if (id == "-1" || id == "draft")
            {
                menu.DefaultMenuAlias = ActionNew.Instance.Alias;
                menu.Items.Add(_localization.Get("general_create"));
            }
            else if (id.IsNumeric())
            {
                menu.Items.Add(_localization.Get("ns_copyToNew"));
                menu.Items.Add(_localization.Get("general_delete"));
            }
 
            menu.Items.Add(_localization.Get("actions_refreshNode"));
 
            return menu;
        }
    }
}

Liked this post? Got a suggestion? Leave a comment