Blog / Web
Build An N-Tier Structured Web Application With .Net MVC Core
  • Jan 13, 2021
  • 54
  • 77

.Net Core is getting more and more popular recently due to the flexibility to work and be hosted in different environments like Windows, Mac, or Linux. In this article, we will learn how to build a .Net MVC Core website in an N-tier project structure.


Advertisement
 


1. The Project Overview - What Are We Building?

We are building a simple .Net MVC Core website to support the requirements of a personal blog that allows users to add, edit information about themselves as well as to post, modify, or archive their articles in this website. This web application does not use external database.

Module 1: Home Page Requirements

  • Display Welcome Message
  • Display Navigation to About Me page
  • Randomly display different Welcome Animated Images


Module 2: About Me Requirements

  • Display the blog's owner information: Name, Biography, Work Experiences, Skills, Education, Portfolio, and Entry to their Blog
  • Allow owner to add, edit, or delete their information
  • Allow owner to upload profile image



Module 3: Blog Requirements

  • Display all articles which are created by the owner
  • Allow owner to add, edit, or archive their posted articles
  • Allow users to search articles by keyword, filter by category, view top popular and favorite articles



Module 4: Contact Requirements

  • Simply show owner contact information



General Requirements

  • Owner should be able to login by their default password
  • Add some animations to  make the website nicer
  • Dynamically created sitemap page for the website
  • Dynamically injected json-ld for article page


2. What Are We Going To Use?

We have all the requirements for this web application. Now we have to decide the technologies we are going to use in order to build our .Net MVC Core application.

Tools:

  • Visual Studio 2019

Server Side:

  1. Net Core 2.0: newer version can be used but since I'm hosting on GoDaddy, I will use Net Core 2.0.
  2. Entity Framework Core
  3. Automapper

Client Side:

  1. Bootstrap 4.3
  2. Summernote
  3. SyntaxHighlighter

3. Create an N-Tire Structured Project

We are building this web application in an N-Tire Structure which has Data, Business, and Web layers.

Step 1: Create a Blank Solution

Open Visual Studio 2019, search for Blank Solution, name the solution "Blog" and then Create:

Step 2: Create a Data Layer

Right click on the Blog solution, Add New Project and Select Class Library (.Net Core). Name the Project "Blog.DAL"



Step 3: Create a Business Layer

Right click on the Blog solution, Add New Project and Select Class Library (.Net Core). Name the Project "Blog.BAL"

Step 4: Create a Web Layer

Right click on the Blog solution, Add New Project and Select ASP.Net Core Web Application. Name the Project "Blog.Web"

Select ASP.Net Core 2.0 and Web Application (Model-View-Controller):

Step 5: Connecting the 3 projects

Add reference to connect the 3 projects in below order:

Blog.DAL >> Blog.BAL >> Blog.Web

To add Project Reference, right click on Project in Solution Explorer, select Add > Project Reference...:



4. Build the .Net MVC Core Application

Ok, let's actually write the code to build the website. We will go to each project and do what we need to do for each:

  • Blog.DAL: This project consists of all entity classes for the application.
  • Blog.BAL: This project consists of all business logic to manipulate the data getting from Blog.DAL project.
  • Blog.Web: This project is to build the front-end part of the application based on the data set returned from the Blog.BAL project.

Step 1: Build Blog.DAL project

Install below package from Manage NuGet Packages:

  • Newtonsoft.Json (12.0.3)

The Blog.DAL project has below structure:

1. In Entities folder, create below classes:

BaseEntity.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
 
namespace Blog.DAL.Entities
{
    public abstract class BaseEntity
    {
        public BaseEntity()
        {
            this.ID = Guid.NewGuid();
            this.CreatedOn = DateTime.UtcNow;
            this.UpdatedOn = DateTime.UtcNow;
            this.IsActive = true;
            this.IsVisible = true;
        }
 
        public Guid ID { get; set; }
        public DateTime CreatedOn { get; set; }
        public DateTime UpdatedOn { get; set; }
        public bool IsActive { get; set; }
        public bool IsVisible { get; set; }
    }
}

Education.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace Blog.DAL.Entities
{
    public class Education : BaseEntity
    {
         
 
        public string School { get; set; }
 
        public string Major { get; set; }
 
        public string Location { get; set; }
 
        public string Duration { get; set; }
 
        public string Items { get; set; }
    }
}

Experience.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace Blog.DAL.Entities
{
    public class Experience : BaseEntity
    {
         
 
        public string Title { get; set; }
 
        public string Company { get; set; }
 
        public string Location { get; set; }
 
        public string Duration { get; set; }
 
        public string Items { get; set; }
    }
}

LdJsonObject.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
using Newtonsoft.Json;
using System.Collections.Generic;
 
namespace Blog.DAL.Entities
{
    public class ArticleLdJson
    {
        public string context { get; set; } = "https://schema.org";
 
        public string type { get; set; } = "NewsArticle";
 
        public MainEntityOfPage mainEntityOfPage { get; set; }
 
        public string headline { get; set; }
 
        public Image image { get; set; }
 
        public string datePublished { get; set; }
 
        public string dateModified { get; set; }
 
        public Author author { get; set; }
 
        public Publisher publisher { get; set; }
 
        public string description { get; set; }
 
        public override string ToString()
        {
            return JsonConvert.SerializeObject(this)
                .Replace("context", "@context")
                .Replace("type", "@type")
                .Replace("id\"", "@id\"");
        }
    }
 
    public class MainEntityOfPage
    {
        public string type { get; set; } = "WebPage";
 
        public string id { get; set; }
    }
 
    public class Image
    {
        public string type { get; set; } = "ImageObject";
 
        public string url { get; set; }
 
        public string width { get; set; } = "700px";
 
        public string height { get; set; } = "400px";
    }
 
    public class Author
    {
        public string type { get; set; } = "Person";
 
        public string name { get; set; }
    }
 
    public class Publisher
    {
        public string type { get; set; } = "Organization";
 
        public string name { get; set; } = "Lucasology";
 
        public Logo logo { get; set; } = new Logo();
    }
 
    public class Logo
    {
        public string type { get; set; } = "ImageObject";
 
        public string url { get; set; }
 
        public string width { get; set; } = "512px";
 
        public string height { get; set; } = "512px";
    }
 
    public class OrganizationLdJson
    {
        public string context { get; set; } = "https://schema.org";
 
        public string type { get; set; } = "Organization";
 
        public string name { get; set; }
 
        public string url { get; set; }
 
        public string logo { get; set; }
 
        public string foundingDate { get; set; }
 
        public List<Author> founders { get; set; }
 
        public List<string> sameAs { get; set; }
 
        public override string ToString()
        {
            return JsonConvert.SerializeObject(this)
                .Replace("context", "@context")
                .Replace("type", "@type")
                .Replace("id", "@id");
        }
    }
 
    public class BreadcrumLdJson
    {
        public string context { get; set; } = "https://schema.org";
 
        public string type { get; set; } = "BreadcrumbList";
 
        public List<BreadcrumElement> itemListElement { get; set; } = new List<BreadcrumElement>();
 
        public override string ToString()
        {
            return JsonConvert.SerializeObject(this)
                .Replace("context", "@context")
                .Replace("type", "@type")
                .Replace("id", "@id");
        }
    }
 
    public class BreadcrumElement
    {
        public string type { get; set; } = "ListItem";
 
        public int position { get; set; }
 
        public string name { get; set; }
 
        public string item { get; set; }
    }
 
    public class CarouselLdJson
    {
        public string context { get; set; } = "https://schema.org";
 
        public string type { get; set; } = "ItemList";
 
        public List<CarouselElement> itemListElement { get; set; } = new List<CarouselElement>();
 
        public override string ToString()
        {
            return JsonConvert.SerializeObject(this)
                .Replace("context", "@context")
                .Replace("type", "@type")
                .Replace("id", "@id");
        }
    }
 
    public class CarouselElement
    {
        public string type { get; set; } = "ListItem";
 
        public int position { get; set; }
 
        public string url { get; set; }
    }
}

Post.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
using System;
using System.Collections.Generic;
 
namespace Blog.DAL.Entities
{
    public class Post : BaseEntity
    {
 
        public string Title { get; set; }
 
        public string Category { get; set; }
 
        public string Series { get; set; }
 
        public string AuthorName { get; set; }
 
        public int LikeCount { get; set; }
 
        public int ViewCount { get; set; }
 
        public string Thumbnail { get; set; }
         
        public string Content { get; set; }
 
        public string Preview { get; set; }
 
        public string Tags { get; set; }
 
        public List<string> Contents { get; set; }
 
        public DateTime PublishedDate { get; set; }
    }
}

Project.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
using System.Collections.Generic;
 
namespace Blog.DAL.Entities
{
    public class Project : BaseEntity
    {
        public string Name { get; set; }
 
        public string Description { get; set; }
 
        public string Image { get; set; }
 
        public ProjectLinks Links { get; set; }
    }
 
    public class ProjectLinks
    {
        public ProjectLinks()
        {
            Links = new List<ProjectLink>();
        }
        public List<ProjectLink> Links { get; set; }
    }
 
    public class ProjectLink : BaseEntity
    {
        public string Link { get; set; }
 
        public string LinkName { get; set; }
    }
}

SitemapUrl.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
 
namespace Blog.DAL.Entities
{
    public enum ChangeFrequency
    {
        Always,
        Hourly,
        Daily,
        Weekly,
        Yearly,
        Never
    }
 
    public class SitemapUrl
    {
        public string Url { get; set; }
        public DateTime? Modified { get; set; }
        public ChangeFrequency? ChangeFrequency { get; set; }
        public double? Priority { get; set; }
    }
 
    public class SitemapBuilder
    {
        private readonly XNamespace NS = "http://www.sitemaps.org/schemas/sitemap/0.9";
        private List<SitemapUrl> _urls;
 
        public SitemapBuilder()
        {
            _urls = new List<SitemapUrl>();
        }
 
        public void AddUrl(string url, DateTime? modified = null, ChangeFrequency? changeFrequency = null, double? priority = null)
        {
            _urls.Add(new SitemapUrl()
            {
                Url = url,
                Modified = modified,
                ChangeFrequency = changeFrequency,
                Priority = priority
            });
        }
 
        public override string ToString()
        {
            var sitemap = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XElement(NS + "urlset",
                    from item in _urls
                    select CreateItemElement(item)
                    ));
 
            return sitemap.ToString();
        }
 
        private XElement CreateItemElement(SitemapUrl url)
        {
            XElement itemElement = new XElement(NS + "url", new XElement(NS + "loc", url.Url));
 
            if(url.Modified.HasValue)
            {
                itemElement.Add(new XElement(NS + "lastmod", url.Modified.Value.ToString("yyyy-MM-dd")));
            }   
 
            if(url.ChangeFrequency.HasValue)
            {
                itemElement.Add(new XElement(NS + "changefreq", url.ChangeFrequency.Value.ToString()));
            }
 
            if (url.Priority.HasValue)
            {
                itemElement.Add(new XElement(NS + "priority", url.Priority.Value.ToString("N1")));
            }
 
            return itemElement;
        }
    }
}

Skill.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace Blog.DAL.Entities
{
    public class Skill : BaseEntity
    {
        public string Category { get; set; }
         
        public string Name { get; set; }
 
        public int? StartYear { get; set; }
 
        public double? Rating { get; set; }
    }
}

User.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections.Generic;
 
namespace Blog.DAL.Entities
{
    public class User : BaseEntity
    {
        public string Name { get; set; }
 
        public string Summary { get; set; }
 
        public string ProfileImage { get; set; }
 
        public List<string> Achievements { get; set; }
 
        public List<Skill> Skills { get; set; }
 
        public List<Experience> Experiences { get; set; }
 
        public List<Education> Educations { get; set; }
 
        public List<Project> Projects { get; set; }
    }
}


Step 2: Build Blog.BAL project

Install below packages from Manage NuGet Packages:

  • HtmlAgilityPack.NetCore (1.5.0.1)
  • Microsoft.AspNetCore.Hosting (2.2.7)
  • Microsoft.AspNetCore.Http (2.2.2)
  • Microsoft.Extensions.Caching.Memory (2.0.2)
  • Microsoft.Extensions.Configuration.Json (3.1.9)
  • Newtonsoft.Json (12.0.3)
The Blog.BAL project has below structure:

1. In Helpers folder, create below classes

AppContext.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
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
 
namespace Blog.BAL.Helpers
{
    public static class MyAppContext
    {
        private static IHttpContextAccessor _httpContextAccessor;
        private static IMemoryCache _cache;
 
        public static void Configure(IHttpContextAccessor httpContextAccessor, IMemoryCache cache)
        {
            _httpContextAccessor = httpContextAccessor;
            _cache = cache;
        }
 
        public static IHttpContextAccessor Current => _httpContextAccessor;
 
        public static void SetupRefreshJob()
        {
            Action remove = _cache.Get("Refresh") as Action;
 
            if (remove is Action)
            {
                _cache.Remove("Refresh");
                remove.EndInvoke(null);
            }
 
            //get the worker
            Action work = () =>
            {
                while (true)
                {
                    Thread.Sleep(60000);
 
                    WebClient refresh = new WebClient();
 
                    try
                    {
                        refresh.UploadString($"http://lucasology.com", string.Empty);
                    }
                    catch (Exception ex)
                    {
                        //Log Error here
                    }
                    finally
                    {
                        refresh.Dispose();
                    }
                }
            };
            Task.Run(() => work.Invoke());
 
            //add this job to cache
            _cache.Set("Refresh", work, new MemoryCacheEntryOptions()
            {
                AbsoluteExpiration = DateTimeOffset.MaxValue,
                SlidingExpiration = TimeSpan.FromDays(365),
                Priority = CacheItemPriority.Normal
            });
        }
    }
}

MySession.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
using Microsoft.AspNetCore.Http;
using System;
 
namespace Blog.BAL.Helpers
{
    public static class MySession
    {
        public static void Set(this IHttpContextAccessor accessor, string key, string value, DateTime expiredDate)
        {
            var cookieOptions = new CookieOptions
            {
                HttpOnly = false,
                IsEssential = true,
                Expires = expiredDate
            };
            accessor.HttpContext.Response.Cookies.Append(key, value, cookieOptions);
        }
 
        public static string Get(this IHttpContextAccessor accessor, string key)
        {
            string val = null;
 
            if (accessor.HttpContext.Request.Cookies[key] != null)
            {
                val = accessor.HttpContext.Request.Cookies[key];
            }
 
            return val;
        }
        public static void Remove(this IHttpContextAccessor accessor, string key)
        {
            if (accessor.HttpContext.Request.Cookies[key] != null)
            {
                accessor.HttpContext.Response.Cookies.Delete(key);
            }
        }
    }
}

PaginatedList.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
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace Blog.BAL.Helpers
{
    public class PaginatedList<T> : List<T>
    {
        public int PageIndex { get; private set; }
        public int TotalPages { get; private set; }
 
        public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);
 
            this.AddRange(items);
        }
 
        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }
 
        public bool HasNextPage
        {
            get
            {
                return (PageIndex < TotalPages);
            }
        }
 
        public static PaginatedList<T> Create(List<T> source, int pageIndex, int pageSize)
        {
            var count = source.Count();
            var items = source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}

Utility.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
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Security.Cryptography;
using System.Text;
 
namespace Blog.BAL.Helpers
{
    public class Utility
    {
        public static string GetValueByKey(string key)
        {
            IConfigurationSection appSetting = AppSetting(key);
            return appSetting.Value;
        }
 
        public static string EncryptPassword(string password, string salt)
        {
            using (var sha256 = SHA256.Create())
            {
                var saltedPassword = string.Format("{0}{1}", salt, password);
                byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
                return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
            }
        }
 
        private static IConfigurationSection AppSetting(string key)
        {
            var configBuilder = new ConfigurationBuilder();
            var path = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
            configBuilder.AddJsonFile(path, false);
            var root = configBuilder.Build();
            var appSetting = root.GetSection(key);
            return appSetting;
        }
 
        public static int YearsOfExperience()
        {
            return DateTime.Now.Year - Convert.ToInt32(Utility.GetValueByKey("StartYear"));
        }
 
        public static double? CalculateSkillRating(double? startYear)
        {
            return ((double)DateTime.Now.Year - (double)startYear) / (double)Utility.YearsOfExperience();
        }
 
        public static string GetUniqueFileName(string fileName)
        {
            fileName = Path.GetFileName(fileName);
            return Path.GetFileNameWithoutExtension(fileName)
                      + "_"
                      + Guid.NewGuid().ToString().Substring(0, 4)
                      + Path.GetExtension(fileName);
        }
 
        public static string ConvertToBase64Image(IFormFile formFile)
        {
            string s = null;
            using(var ms = new MemoryStream())
            {
                formFile.CopyTo(ms);
                var fileBytes = ms.ToArray();
                s = Convert.ToBase64String(fileBytes);
            }
            return s;
        }
 
        public static bool IsBase64(string base64String)
        {
            if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
                || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
                    return false;
            try
            {
                Convert.FromBase64String(base64String);
                return true;
            }
            catch
            {
                // Handle the exception
            }
            return false;
        }
 
        public static string ConvertBase64ToImage(string base64Img, string path)
        {
            string profileImageName = Guid.NewGuid() + ".jpg";
 
            //Convert Base64 Encoded string to Byte Array.
            byte[] imageBytes = Convert.FromBase64String(base64Img);
 
            //Save the Byte Array as Image File.
            var imgFilePath = Path.Combine(path, profileImageName);
            File.WriteAllBytes(imgFilePath, imageBytes);
 
            return profileImageName;
        }
    }
}

2. In Implementation folder, create below classes

AchievementManager.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
using Blog.BAL.Interfaces;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class AchievementManager : IAchievementManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public AchievementManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private List<string> ConvertToDTO(List<string> model)
        {
            List<string> achievements = new List<string>();
            foreach(var s in model)
            {
                achievements.Add(s);
            }
            return achievements;
        }
        #endregion
 
        #region public methods
        public void EditAchievements(List<string> model)
        {
            string filePath = $"{contentRootPath}\\Database\\Achievements.json";
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public List<string> GetAchievements()
        {
            string filePath = $"{contentRootPath}\\Database\\Achievements.json";
            List<string> achievements = new List<string>();
 
            if (File.Exists(filePath))
                achievements = JsonConvert.DeserializeObject<List<string>>(System.IO.File.ReadAllText(filePath));
 
            return achievements;
        }
        #endregion
    }
}

BlogManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.BAL.Helpers;
using Blog.DAL.Entities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using System.Linq;
using System.Net;
using HtmlAgilityPack;
using System.Text.RegularExpressions;
using System.Web;
 
namespace Blog.BAL.Implementations
{
    public class BlogManager : IBlogManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public BlogManager(IHostingEnvironment hostingEnvironment)
        {
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region public methods
        public void Create(PostViewModel model)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
 
            ProcessPost(model);
 
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath));
 
            var newPost = ConvertToDTO(model);
            posts.Add(ConvertToDTO(model));
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(posts));
        }
        #endregion
 
        #region public methods
        public void Edit(PostViewModel model)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
             
            Regex tagRegex = new Regex(@"<[^>]+>");
            if(tagRegex.IsMatch(model.Content))
                ProcessPost(model);
 
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath));
 
            var index = posts.FindIndex(p => p.ID == model.ID);
            model.Thumbnail = model.Thumbnail == null ? posts[index].Thumbnail : model.Thumbnail;
            posts[index] = ConvertToDTO(model);
            posts[index].UpdatedOn = DateTime.UtcNow;
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(posts));
        }
 
        public List<Post> GetPosts(int? take)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath))
                    .Where(p => p.PublishedDate <= DateTime.UtcNow && p.IsActive)
                    .OrderByDescending(p => p.UpdatedOn).ToList();
 
            return take == null ? posts : posts.Take((int)take).ToList();
        }
 
        public List<Post> GetAllPosts(int? take)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath))
                    .OrderByDescending(p => p.UpdatedOn).ToList();
 
            return take == null ? posts : posts.Take((int)take).ToList();
        }
         
        public Post GetPostByID(Guid ID)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath));
 
            return posts.Where(p => p.ID == ID).FirstOrDefault();
        }
 
        public Post GetPostByUrl(string url)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath));
 
            return string.IsNullOrEmpty(url)? null : posts.Where(p => p.Content.ToUpper() == HttpUtility.UrlDecode(url).ToUpper() && p.IsActive).FirstOrDefault();
        }
 
        public Post GetPostByUrlForArchival(string url)
        {
            string filePath = $"{contentRootPath}\\Database\\Posts.json";
            List<Post> posts = new List<Post>();
 
            if (File.Exists(filePath))
                posts = JsonConvert.DeserializeObject<List<Post>>(System.IO.File.ReadAllText(filePath));
 
            return string.IsNullOrEmpty(url) ? null : posts.Where(p => p.Content.ToUpper() == HttpUtility.UrlDecode(url).ToUpper()).FirstOrDefault();
        }
 
        public string GetContentByUrl(string url)
        {
            string filePath = $"{contentRootPath}\\Blog\\{url}.html";
 
            string content = "";
            if (File.Exists(filePath))
                content = File.ReadAllText(filePath);
 
            return content;
        }
        #endregion
 
        #region private methods
 
        private void ProcessPost(PostViewModel model)
        {
            //ProcessThumbnail
            if (model.FormFile != null)
            {
                var uniqueFileName = Utility.GetUniqueFileName(model.FormFile.FileName);
                var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads/BlogImages");
                var imgFilePath = Path.Combine(uploads, uniqueFileName);
                model.FormFile.CopyTo(new FileStream(imgFilePath, FileMode.Create));
                model.Thumbnail = uniqueFileName;
            }
 
            //ProcessContentHtml
             ProcessContentHTML(model);
 
            //ProcessContent
            WriteHTML(model);
        }
 
        private void WriteHTML(PostViewModel model)
        {
            string uniqueTitle;
 
            //Make sure not create new html file while editing
            if (model.ID == Guid.Empty)
                uniqueTitle = Utility.GetUniqueFileName(model.Title);
            else
                uniqueTitle = this.GetPostByID(model.ID).Content;
 
            string filePath = $"{contentRootPath}\\Blog\\" + uniqueTitle + ".html";
 
            File.WriteAllText(filePath, model.Content);
 
            model.Preview = Regex.Replace(model.Content, "<.*?>", String.Empty);
            if (model.Preview.Length > 200)
                model.Preview = model.Preview.Substring(0, 200);
 
            model.Content = uniqueTitle;
        }
 
        private string ProcessContentHTML(PostViewModel model)
        {
            string html;
            using (WebClient client = new WebClient())
            {
                html = model.Content;
            }
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            try
            {
 
                if (model.Content.Contains("img"))
                {
                    foreach (HtmlNode img in doc.DocumentNode.SelectNodes("//img"))
                    {
                        var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads/BlogImages");
                        var base64Img = img.GetAttributeValue("src", null);
                        if(!base64Img.Contains("Uploads"))
                            base64Img = base64Img.Split(';')[1].Split(',')[1].ToString();
                        if (!string.IsNullOrEmpty(base64Img) && Utility.IsBase64(base64Img))
                        {
                            var imgSrc = Utility.ConvertBase64ToImage(base64Img, uploads);
                            img.SetAttributeValue("src", "/Uploads/BlogImages/" + imgSrc);
                        }
                    }
                }
 
                int i = 0;
                foreach (HtmlNode hNode in doc.DocumentNode.SelectNodes("//h1"))
                {
                    model.Contents.Add(hNode.InnerText);
                    hNode.SetAttributeValue("id", "section_" + i);
                    i++;
                }
 
                using (StringWriter writer = new StringWriter())
                {
                    doc.Save(writer);
                    model.Content = writer.ToString();
                }
            }
            catch
            {
                return model.Content;
            }
 
            return model.Content;
        }
 
        private Post ConvertToDTO(PostViewModel model)
        {
            Post post = new Post()
            {
                ID = model.ID == Guid.Empty ? Guid.NewGuid() : model.ID,
                ViewCount = model.ID == Guid.Empty ? new Random().Next(50, 100) : model.ViewCount,
                LikeCount = model.ID == Guid.Empty ? new Random().Next(50, 100) : model.LikeCount,
                Title = model.Title,
                Category = model.Category,
                Series = model.Series,
                AuthorName = model.AuthorName,
                Thumbnail = model.Thumbnail,
                Preview = model.Preview,
                Content = model.Content,
                Tags = model.TagsString,
                Contents = model.Contents,
                PublishedDate = model.PublishedDate,
                IsActive = model.IsActive
            };
 
            return post;
        }
        #endregion
    }
}

EducationManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class EducationManager : IEducationManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public EducationManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private List<Education> ConvertToDTO(List<EducationViewModel> model)
        {
            List<Education> educations = new List<Education>();
            foreach(var e in model)
            {
                Education education = new Education()
                {
                    School = e.School,
                    Major = e.Major,
                    Location = e.Location,
                    Duration = e.Duration,
                    Items = e.Items
                };
                educations.Add(education);
            }
            return educations;
        }
        #endregion
 
        #region public methods
        public void EditEducations(List<EducationViewModel> model)
        {
            string filePath = $"{contentRootPath}\\Database\\Educations.json";
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public List<Education> GetEducations()
        {
            string filePath = $"{contentRootPath}\\Database\\Educations.json";
            List<Education> educations = new List<Education>();
 
            if (File.Exists(filePath))
                educations = JsonConvert.DeserializeObject<List<Education>>(System.IO.File.ReadAllText(filePath));
 
            return educations;
        }
 
        public void EditEducations(List<EducationsViewModel> model)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}

ExperienceManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class EducationManager : IEducationManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public EducationManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private List<Education> ConvertToDTO(List<EducationViewModel> model)
        {
            List<Education> educations = new List<Education>();
            foreach(var e in model)
            {
                Education education = new Education()
                {
                    School = e.School,
                    Major = e.Major,
                    Location = e.Location,
                    Duration = e.Duration,
                    Items = e.Items
                };
                educations.Add(education);
            }
            return educations;
        }
        #endregion
 
        #region public methods
        public void EditEducations(List<EducationViewModel> model)
        {
            string filePath = $"{contentRootPath}\\Database\\Educations.json";
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public List<Education> GetEducations()
        {
            string filePath = $"{contentRootPath}\\Database\\Educations.json";
            List<Education> educations = new List<Education>();
 
            if (File.Exists(filePath))
                educations = JsonConvert.DeserializeObject<List<Education>>(System.IO.File.ReadAllText(filePath));
 
            return educations;
        }
 
        public void EditEducations(List<EducationsViewModel> model)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}

ExperienceManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class ExperienceManager : IExperienceManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public ExperienceManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private List<Experience> ConvertToDTO(List<ExperienceViewModel> model)
        {
            List<Experience> experiences = new List<Experience>();
            foreach(var exp in model)
            {
                Experience experience = new Experience()
                {
                    Title = exp.Title,
                    Company = exp.Company,
                    Location = exp.Location,
                    Duration = exp.Duration,
                    Items = exp.Items
                };
                experiences.Add(experience);
            }
            return experiences;
        }
        #endregion
 
        #region public methods
        public void EditExperiences(List<ExperienceViewModel> model)
        {
            string filePath = $"{contentRootPath}\\Database\\Experiences.json";
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public List<Experience> GetExperiences()
        {
            string filePath = $"{contentRootPath}\\Database\\Experiences.json";
            List<Experience> experiences = new List<Experience>();
 
            if (File.Exists(filePath))
                experiences = JsonConvert.DeserializeObject<List<Experience>>(System.IO.File.ReadAllText(filePath));
 
            return experiences;
        }
        #endregion
    }
}

LdObjectManager.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
using Blog.BAL.Interfaces;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class LdObjectManager : ILdObjectManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public LdObjectManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        public string GetArticleLdObject(Post post)
        {
            ArticleLdJson article = new ArticleLdJson()
            {
                mainEntityOfPage = new MainEntityOfPage()
                {
                    id = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/?url=" + post.Content
                },
                headline = post.Title,
                image = new Image()
                {
                    url = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/Uploads/BlogImages/" + post.Thumbnail
                },
                datePublished = post.PublishedDate.ToString("yyyy-MM-dd"),
                dateModified = post.UpdatedOn.ToString("yyyy-MM-dd"),
                author = new Author()
                {
                    name = post.AuthorName
                },
                publisher = new Publisher()
                {
                    logo = new Logo()
                    {
                        url = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/Uploads/UserImages/blog-icon_269d.jpg"
                    }
                },
                description = post.Preview + "..."
            };
 
            return article.ToString();
        }
 
        public string GetOrganizationLdObject()
        {
            List<Author> authors = new List<Author>();
            authors.Add(new Author() {
                name = "Hoang (Lucas) Nguyen"
            });
 
            List<string> sameAs = new List<string>();
            sameAs.Add("https://www.linkedin.com/in/nguyenhm/");
            sameAs.Add("https://github.com/lucas-ngminh");
            sameAs.Add("https://www.youtube.com/channel/UCMc-1Gb8xFkyUIlwqpPmS1Q");
            sameAs.Add("https://www.facebook.com/lucasology");
            sameAs.Add("https://www.instagram.com/lucas.ology");
 
            OrganizationLdJson org = new OrganizationLdJson()
            {
                name = "Lucasology",
                url = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}",
                logo = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/Uploads/UserImages/blog-icon_269d.jpg",
                foundingDate = "2020",
                founders = authors,
                sameAs = sameAs
            };
 
            return org.ToString();
        }
 
        public string GetBreadcrumLdObject(List<string> names, List<string> url)
        {
            List<BreadcrumElement> elements = new List<BreadcrumElement>();
            for (int i = 0; i < names.Count; i++)
            {
                BreadcrumElement e = new BreadcrumElement()
                {
                    position = i + 1,
                    name = names[i],
                    item = url[i]
                };
                elements.Add(e);
            }
 
            BreadcrumLdJson breadcrum = new BreadcrumLdJson()
            {
                itemListElement =elements
            };
 
            return breadcrum.ToString();
        }
 
        public string GetCarouselLdObject(List<Post> posts)
        {
            List<CarouselElement> elements = new List<CarouselElement>();
 
            int i = 0;
            foreach(var p in posts)
            {
                CarouselElement e = new CarouselElement()
                {
                    position = i + 1,
                    url = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/?url=" + p.Content
                };
                elements.Add(e);
                i++;
            }
 
            CarouselLdJson carousel = new CarouselLdJson()
            {
                itemListElement = elements
            };
 
            return carousel.ToString();
        }
    }
}

ProjectManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.BAL.Helpers;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class ProjectManager : IProjectManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public ProjectManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private List<Project> ConvertToDTO(List<ProjectViewModel> model)
        {
            List<Project> projects = new List<Project>();
            foreach(var prj in model)
            {
                Project project = new Project()
                {
                    Name = prj.Name,
                    Description = prj.Description,
                    Image = prj.Image,
                    Links = ConvertToDTO(prj.Links)
                };
                projects.Add(project);
            }
            return projects;
        }
 
        private ProjectLinks ConvertToDTO(ProjectLinksViewModel links)
        {
            ProjectLinks plList = new ProjectLinks();
            foreach(var l in links.Links)
            {
                ProjectLink pl = new ProjectLink()
                {
                    Link = l.Link,
                    LinkName = l.LinkName
                };
                plList.Links.Add(pl);
            }
            return plList;
        }
        #endregion
 
        #region public methods
        public void EditProjects(List<ProjectViewModel> model)
        {
            string filePath = $"{contentRootPath}\\Database\\Projects.json";
 
            foreach(var p in model)
            {
                if (!string.IsNullOrEmpty(p.Image) && Utility.IsBase64(p.Image))
                {
                    var uniqueFileName = Utility.GetUniqueFileName(p.ImageFileName);
 
                    //Convert Base64 Encoded string to Byte Array.
                    byte[] imageBytes = Convert.FromBase64String(p.Image);
 
                    //Save the Byte Array as Image File.
                    var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads/ProjectImages");
                    var imgFilePath = Path.Combine(uploads, uniqueFileName);
                    File.WriteAllBytes(imgFilePath, imageBytes);
 
                    p.Image = uniqueFileName;
                }
                if(p.FormFile != null)
                {
                    var uniqueFileName = Utility.GetUniqueFileName(p.FormFile.FileName);
                    var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads/ProjectImages");
                    var imgFilePath = Path.Combine(uploads, uniqueFileName);
                    p.FormFile.CopyTo(new FileStream(imgFilePath, FileMode.Create));
                    p.Image = uniqueFileName;
                }
            }
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public List<Project> GetProjects()
        {
            string filePath = $"{contentRootPath}\\Database\\Projects.json";
            List<Project> projects = new List<Project>();
 
            if (File.Exists(filePath))
                projects = JsonConvert.DeserializeObject<List<Project>>(System.IO.File.ReadAllText(filePath));
 
            return projects;
        }
        #endregion
    }
}

SkillManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class SkillManager : ISkillManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public SkillManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private List<Skill> ConvertToDTO(List<SkillViewModel> model)
        {
            List<Skill> skills = new List<Skill>();
            foreach(var s in model)
            {
                Skill skill = new Skill()
                {
                    Category = s.Category,
                    Name = s.Name,
                    StartYear = Convert.ToInt32(s.StartYear),
                    Rating = s.Rating
                };
                skills.Add(skill);
            }
            return skills;
        }
        #endregion
 
        #region public methods
        public void EditSkills(List<SkillViewModel> model)
        {
            string filePath = $"{contentRootPath}\\Database\\Skills.json";
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public List<Skill> GetSkills()
        {
            string filePath = $"{contentRootPath}\\Database\\Skills.json";
            List<Skill> skills = new List<Skill>();
 
            if (File.Exists(filePath))
                skills = JsonConvert.DeserializeObject<List<Skill>>(System.IO.File.ReadAllText(filePath));
 
            return skills;
        }
        #endregion
    }
}

UserManager.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
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.BAL.Helpers;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.BAL.Implementations
{
    public class UserManager : IUserManager
    {
        private readonly string contentRootPath = Directory.GetCurrentDirectory();
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHostingEnvironment _hostingEnvironment;
 
        public UserManager(IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment)
        {
            _httpContextAccessor = httpContextAccessor;
            _hostingEnvironment = hostingEnvironment;
        }
 
        #region private methods
        private User ConvertToDTO(EditAboutMeViewModel model)
        {
            User user = new User() {
                Name = model.Name,
                Summary = model.Summary,
                ProfileImage = model.ProfileImage,
                //Achievements = model.EditAchievement.Achievements,
                //Skills = ConvertToDTO(model.EditSkill.Skills),
                //Experiences = ConvertToDTO(model.EditExperience.Experiences),
                //Educations = ConvertToDTO(model.EditEducation.Educations)
            };
 
            if(string.IsNullOrEmpty(model.ProfileImage))
            {
                var owner = GetOwner();
                user.ProfileImage = owner.ProfileImage;
            }
 
            return user;
        }
 
        private List<Education> ConvertToDTO(List<EducationViewModel> educations)
        {
            List<Education> eduList = new List<Education>();
            foreach(var e in educations)
            {
                eduList.Add(new Education() {
                    School = e.School,
                    Major = e.Major,
                    Location = e.Location,
                    Duration = e.Duration,
                    Items = e.Items
                });
            }
 
            return eduList;
        }
 
        private List<Experience> ConvertToDTO(List<ExperienceViewModel> experiences)
        {
            List<Experience> expList = new List<Experience>();
            foreach (var e in experiences)
            {
                expList.Add(new Experience()
                {
                    Title = e.Title,
                    Company = e.Company,
                    Location = e.Location,
                    Duration = e.Duration,
                    Items = e.Items
                });
            }
 
            return expList;
        }
 
        private List<Skill> ConvertToDTO(List<SkillViewModel> skills)
        {
            List<Skill> sList = new List<Skill>();
            foreach (var s in skills)
            {
                sList.Add(new Skill()
                {
                    Category = s.Category,
                    Name = s.Name,
                    StartYear = Convert.ToInt32(s.StartYear),
                    Rating = s.Rating
                });
            }
 
            return sList;
        }
 
        public bool ValidateUser(string password)
        {
            var valid = string.Equals(Utility.EncryptPassword(password, Utility.GetValueByKey("Salt")),
                Utility.GetValueByKey("HashedPassword"));
 
            return valid;
        }
        #endregion
 
        #region public methods
        public void EditAboutMe(EditAboutMeViewModel model)
        {
            string filePath = $"{contentRootPath}\\Database\\Owner.json";
 
            if (model.FormFile != null)
            {
                var uniqueFileName = Utility.GetUniqueFileName(model.FormFile.FileName);
                var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads/UserImages");
                var imgFilePath = Path.Combine(uploads, uniqueFileName);
                model.FormFile.CopyTo(new FileStream(imgFilePath, FileMode.Create));
                model.ProfileImage = uniqueFileName;
            }
 
            File.WriteAllText(filePath, JsonConvert.SerializeObject(ConvertToDTO(model)));
        }
 
        public User GetOwner()
        {
            string ownerFilePath = $"{contentRootPath}\\Database\\Owner.json";
            string experienceFilePath = $"{contentRootPath}\\Database\\Experiences.json";
            string skillFilePath = $"{contentRootPath}\\Database\\Skills.json";
            string educationFilePath = $"{contentRootPath}\\Database\\Educations.json";
            string achievementFilePath = $"{contentRootPath}\\Database\\Achievements.json";
            string projectFilePath = $"{contentRootPath}\\Database\\Projects.json";
            User user = null;
 
            if (File.Exists(ownerFilePath))
                user = JsonConvert.DeserializeObject<User>(System.IO.File.ReadAllText(ownerFilePath));
 
            if (user != null && File.Exists(experienceFilePath))
                user.Experiences = JsonConvert.DeserializeObject<List<Experience>>(System.IO.File.ReadAllText(experienceFilePath));
 
            if (user != null && File.Exists(skillFilePath))
                user.Skills = JsonConvert.DeserializeObject<List<Skill>>(System.IO.File.ReadAllText(skillFilePath));
 
            if (user != null && File.Exists(educationFilePath))
                user.Educations = JsonConvert.DeserializeObject<List<Education>>(System.IO.File.ReadAllText(educationFilePath));
 
            if (user != null && File.Exists(achievementFilePath))
                user.Achievements = JsonConvert.DeserializeObject<List<string>>(System.IO.File.ReadAllText(achievementFilePath));
 
            if (user != null && File.Exists(achievementFilePath))
                user.Achievements = JsonConvert.DeserializeObject<List<string>>(System.IO.File.ReadAllText(achievementFilePath));
 
            if (user != null && File.Exists(projectFilePath))
                user.Projects = JsonConvert.DeserializeObject<List<Project>>(System.IO.File.ReadAllText(projectFilePath));
 
            return user;
        }
 
        public bool Login(LoginViewModel model)
        {
            bool valid = false;
            if (ValidateUser(model.Password))
                valid = true;
 
            return valid;
        }
        #endregion
    }
}

3. In Interfaces folder, create below classes

IAchievementManager.cs

1
2
3
4
5
6
7
8
9
10
11
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface IAchievementManager
    {
        void EditAchievements(List<string> model);
        List<string> GetAchievements();
    }
}

IBlogManager.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
using Blog.BAL.Models;
using Blog.DAL.Entities;
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface IBlogManager
    {
        void Create(PostViewModel model);
 
        List<Post> GetPosts(int? take);
 
        Post GetPostByUrl(string url);
 
        string GetContentByUrl(string url);
 
        void Edit(PostViewModel model);
 
        Post GetPostByUrlForArchival(string url);
 
        List<Post> GetAllPosts(int? take);
    }
}

IEducationManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using Blog.BAL.Models;
using Blog.DAL.Entities;
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface IEducationManager
    {
        void EditEducations(List<EducationViewModel> model);
        List<Education> GetEducations();
    }
}

IExperienceManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using Blog.BAL.Models;
using Blog.DAL.Entities;
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface IExperienceManager
    {
        void EditExperiences(List<ExperienceViewModel> model);
        List<Experience> GetExperiences();
    }
}

ILdObjectManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using Blog.DAL.Entities;
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface ILdObjectManager
    {
        string GetArticleLdObject(Post post);
        string GetOrganizationLdObject();
        string GetBreadcrumLdObject(List<string> names, List<string> urls);
        string GetCarouselLdObject(List<Post> posts);
    }
}

IProjectManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using Blog.BAL.Models;
using Blog.DAL.Entities;
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface IProjectManager
    {
        void EditProjects(List<ProjectViewModel> model);
        List<Project> GetProjects();
    }
}

ISkillManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using Blog.BAL.Models;
using Blog.DAL.Entities;
using System.Collections.Generic;
 
namespace Blog.BAL.Interfaces
{
    public interface ISkillManager
    {
        void EditSkills(List<SkillViewModel> model);
        List<Skill> GetSkills();
    }
}

IUserManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
using Blog.BAL.Models;
using Blog.DAL.Entities;
 
namespace Blog.BAL.Interfaces
{
    public interface IUserManager
    {
        void EditAboutMe(EditAboutMeViewModel user);
        User GetOwner();
        bool Login(LoginViewModel model);
    }
}

3. In Models folder, create below classes

AchievementViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class EditAchievementViewModel
    {
        public EditAchievementViewModel()
        {
            Achievements = new List<string>();
        }
 
        [Required]
        public string Achievement { get; set; }
 
        public List<string> Achievements { get; set; }
    }
 
    public class AchievementsViewModel
    {
        public List<string> Achievements { get; set; }
    }
}

BlogViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using Blog.BAL.Helpers;
using System.Collections.Generic;
 
namespace Blog.BAL.Models
{
    public class BlogViewModel
    {
        public List<string> Categories { get; set; }
 
        public PaginatedList<PostViewModel> PagedBlog { get; set; }
 
        public List<PostViewModel> FavoritePosts { get; set; }
 
        public List<PostViewModel> PopularPosts { get; set; }
 
        public UserViewModel AboutMe { get; set; }
    }
}

EducationViewModel.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
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class EducationViewModel
    {
        public Guid ID { get; set; }
 
        public string School { get; set; }
 
        public string Major { get; set; }
 
        public string Location { get; set; }
 
        public string Duration { get; set; }
 
        public string Items { get; set; }
    }
 
    public class EducationsViewModel
    {
        public List<EducationViewModel> Educations { get; set; }
    }
 
    public class EditEducationViewModel
    {
        public EditEducationViewModel()
        {
            Educations = new List<EducationViewModel>();
        }
 
 
        [Required]
        public string School { get; set; }
 
 
        [Required]
        public string Major { get; set; }
 
 
        [Required]
        public string Location { get; set; }
 
 
        [Required]
        public string Duration { get; set; }
 
 
        [Required]
        public string Items { get; set; }
 
        public List<EducationViewModel> Educations { get; set; }
    }
}

ExperienceViewModel.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
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class EducationViewModel
    {
        public Guid ID { get; set; }
 
        public string School { get; set; }
 
        public string Major { get; set; }
 
        public string Location { get; set; }
 
        public string Duration { get; set; }
 
        public string Items { get; set; }
    }
 
    public class EducationsViewModel
    {
        public List<EducationViewModel> Educations { get; set; }
    }
 
    public class EditEducationViewModel
    {
        public EditEducationViewModel()
        {
            Educations = new List<EducationViewModel>();
        }
 
 
        [Required]
        public string School { get; set; }
 
 
        [Required]
        public string Major { get; set; }
 
 
        [Required]
        public string Location { get; set; }
 
 
        [Required]
        public string Duration { get; set; }
 
 
        [Required]
        public string Items { get; set; }
 
        public List<EducationViewModel> Educations { get; set; }
    }
}

LoginViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class LoginViewModel
    {
        [Required]
        public string Password { get; set; }
 
        public bool RememberMe { get; set; }
    }
}

PostViewModel.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
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class PostViewModel
    {
        public Guid ID { get; set; }
 
        [Required]
        public string Title { get; set; }
 
        public string Series { get; set; }
 
        public int ViewCount { get; set; }
 
        public int LikeCount { get; set; }
 
        [Required]
        public string Category { get; set; }
 
        [Required]
        public string AuthorName { get; set; } = "Lucas";
 
        [Display(Name = "Thumbnail")]
        public IFormFile FormFile { get; set; }
 
        public string Thumbnail { get; set; }
 
        [Required]
        public string Content { get; set; }
 
        public string Preview { get; set; }
 
        [Required]
        public string TagsString { get; set; }
 
        public List<string> Tags { get; set; }
 
        [Required]
        public DateTime PublishedDate { get; set; } = DateTime.Now;
 
        public List<string> Contents { get; set; } = new List<string>();
 
        public List<PostViewModel> SeriesPosts { get; set; } = new List<PostViewModel>();
 
        public bool IsActive { get; set; }
    }
}

ProjectViewModel.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
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class ProjectViewModel
    {
        public ProjectViewModel()
        {
            Links = new ProjectLinksViewModel();
        }
        public Guid ID { get; set; }
 
        [Display(Name = "Profile Image")]
        public IFormFile FormFile { get; set; }
 
        public string Image { get; set; }
 
        public string ImageFileName { get; set; }
 
        [Required]
        public string Name { get; set; }
 
        [Required]
        public string Description { get; set; }
 
        public ProjectLinksViewModel Links { get; set; }
    }
 
    public class ProjectsViewModel
    {
        public List<ProjectViewModel> Projects { get; set; }
    }
     
    public class ProjectLinkViewModel
    {
        [Required]
        public string Link { get; set; }
 
        [Required]
        public string LinkName { get; set; }
    }
 
    public class ProjectLinksViewModel
    {
        public ProjectLinksViewModel()
        {
            Links = new List<ProjectLinkViewModel>();
        }
        public List<ProjectLinkViewModel> Links { get; set; }
    }
 
    public class EditPortfolioViewModel
    {
        public EditPortfolioViewModel()
        {
            Links = new ProjectLinksViewModel();
            Projects = new List<ProjectViewModel>();
        }
 
        [Display(Name = "Project Image")]
        public IFormFile FormFile { get; set; }
 
        public string Image { get; set; }
 
        public string ImageFileName { get; set; }
 
        [Required]
        public string Name { get; set; }
         
        [Required]
        public string Description { get; set; }
 
        public ProjectLinksViewModel Links { get; set; }
 
        public List<ProjectViewModel> Projects { get; set; }
    }
}

SkillViewModel.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
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class SkillViewModel
    {
        public Guid ID { get; set; }
 
        [Required]
        public string Category { get; set; }
 
        [Required]
        public string Name { get; set; }
 
        [Required]
        public double? StartYear { get; set; }
 
        public double? Rating { get; set; }
    }
 
    public class SkillsViewModel
    {
        public List<SkillViewModel> Skills { get; set; }
    }
 
    public class EditSkillViewModel
    {
        public EditSkillViewModel()
        {
            Skills = new List<SkillViewModel>();
        }
 
        [Required]
        public string Category { get; set; }
 
        [Required]
        public string Name { get; set; }
 
        [Required]
        public double? StartYear { get; set; }
 
        public double? Rating { get; set; }
 
        public List<SkillViewModel> Skills { get; set; }
    }
}

UserViewModel.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
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace Blog.BAL.Models
{
    public class UserViewModel
    {
        public Guid ID { get; set; }
 
        public string ProfileImage { get; set; }
 
        public string Name { get; set; }
 
        public string Summary { get; set; }
 
        public List<string> Achievements { get; set; }
 
        public List<SkillViewModel> Skills { get; set; }
 
        public List<ExperienceViewModel> Experiences { get; set; }
 
        public List<EducationViewModel> Educations { get; set; }
 
        public List<ProjectViewModel> Projects { get; set; }
 
        public List<PostViewModel> Posts { get; set; }
    }
 
    public class EditUserViewModel
    {
        public EditUserViewModel()
        {
            EditAchievement = new EditAchievementViewModel();
            EditSkill = new EditSkillViewModel();
            EditExperience = new EditExperienceViewModel();
            EditEducation = new EditEducationViewModel();
            EditPortfolio = new EditPortfolioViewModel();
        }
        public Guid ID { get; set; }
 
        public EditAboutMeViewModel EditAboutMe { get; set; }
         
        public EditAchievementViewModel EditAchievement { get; set; }
 
        public EditSkillViewModel EditSkill { get; set; }
 
        public EditExperienceViewModel EditExperience { get; set; }
 
        public EditEducationViewModel EditEducation { get; set; }
 
        public EditPortfolioViewModel EditPortfolio { get; set; }
    }
 
    public class EditAboutMeViewModel
    {
        [Display(Name = "Profile Image")]
        public IFormFile FormFile { get; set; }
 
        public string ProfileImage { get; set; }
 
        [Required]
        public string Name { get; set; }
 
        [Required]
        public string Summary { get; set; }
    }
}

Step 3: Build Blog.Web project

Install below package from Manage NuGet Packages:

  • AutoMapper.Extensions.Microsoft.DependencyInjection (8.1.0)
  • Microsoft.AspNetCore.All (2.0.9)
  • Microsoft.VisualStudio.Web.CodeGeneration.Design (2.0.4)

Install below client library by right click on project >> Add >> Client Side Library:

  • bootstrap 4.3.1
  • font-awesome
  • summernote
  • SyntaxHighlighter


This project has below structure:

Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
 
namespace Blog.Web
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }
 
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

Startup.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
using System;
using AutoMapper;
using Blog.BAL.Helpers;
using Blog.BAL.Implementations;
using Blog.BAL.Interfaces;
using Blog.Web.Infrastructures;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
 
namespace Blog.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
 
        public IConfiguration Configuration { get; }
 
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //AutoMapper
            services.AddAutoMapper(typeof(Startup));
 
            //Dependency Injection
            services.AddScoped<IUserManager, UserManager>();
            services.AddScoped<ISkillManager, SkillManager>();
            services.AddScoped<IAchievementManager, AchievementManager>();
            services.AddScoped<IEducationManager, EducationManager>();
            services.AddScoped<IExperienceManager, ExperienceManager>();
            services.AddScoped<IProjectManager, ProjectManager>();
            services.AddScoped<IBlogManager, BlogManager>();
            services.AddScoped<ILdObjectManager, LdObjectManager>();
 
            //HttpContextAccessor
            services.AddHttpContextAccessor();
 
            services.Configure<SecurityStampValidatorOptions>(options => {
                options.ValidationInterval = TimeSpan.FromDays(365);
            });
 
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(config =>
            {
                config.Cookie.Name = "__MyBlogCookie__";
                config.LoginPath = "/Home/Login";
                config.SlidingExpiration = true;
                config.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                config.Cookie.Expiration = TimeSpan.FromSeconds(10);
                config.ExpireTimeSpan = TimeSpan.FromSeconds(10);
            });
 
            services.AddAuthorization();
 
            //AMP
            services.Configure<RazorViewEngineOptions>(options =>
            {
                options.ViewLocationExpanders.Add(new AmpViewLocationExpander());
            });
 
            services.AddMvc(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                            .RequireAuthenticatedUser()
                            .Build();
            });
        }
 
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
 
            app.UseStaticFiles();
 
            app.UseAuthentication();
 
            MyAppContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>(),
                app.ApplicationServices.GetRequiredService<IMemoryCache>());
 
            MyAppContext.SetupRefreshJob();
 
            app.UseStatusCodePagesWithReExecute("/Error/{0}");
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

appsettings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "StartYear": "2015",
  "HashedPassword": "Your Password",
  "Salt": "Your Salt",
  "Version": "1.0.0"
}


1. In Controllers folder, create below controllers

AboutMeController.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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
using AutoMapper;
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.BAL.Helpers;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
 
namespace Blog.Web.Controllers
{
    public class AboutMeController : Controller
    {
        private readonly IConfiguration _config;
        private readonly IUserManager _userManager;
        private readonly ISkillManager _skillManager;
        private readonly IExperienceManager _experienceManager;
        private readonly IEducationManager _educationManager;
        private readonly IAchievementManager _achievementManager;
        private readonly IProjectManager _projectManager;
        private readonly IBlogManager _blogManager;
        private readonly ILdObjectManager _ldObjectManager;
        private readonly IMapper _mapper;
        private readonly IHttpContextAccessor _httpContextAccessor;
 
        public AboutMeController(IConfiguration config, IUserManager userManager, ISkillManager skillManager,
            IExperienceManager experienceManager, IEducationManager educationManager, IAchievementManager achievementManager,
            IProjectManager projectManager, IBlogManager blogManager, ILdObjectManager ldObjectManager,
            IMapper mapper, IHttpContextAccessor httpContextAccessor)
        {
            _config = config;
            _userManager = userManager;
            _skillManager = skillManager;
            _experienceManager = experienceManager;
            _educationManager = educationManager;
            _achievementManager = achievementManager;
            _projectManager = projectManager;
            _blogManager = blogManager;
            _ldObjectManager = ldObjectManager;
            _mapper = mapper;
            _httpContextAccessor = httpContextAccessor;
        }
 
        // GET: AboutMeController
        public ActionResult Index()
        {
            User owner = _userManager.GetOwner();
            var model = _mapper.Map<UserViewModel>(owner);
            model.Posts = _mapper.Map<List<PostViewModel>>(_blogManager.GetPosts(3));
 
            ViewBag.LdJsonObject = _ldObjectManager.GetOrganizationLdObject();
 
            ViewBag.ArticleTitle = " About Me";
            ViewBag.Thumbnail = "/images/LucaSharp-big.jpg";
            ViewBag.TwitterThumbnail = $"http://{_httpContextAccessor.HttpContext.Request.Host.Value}/images/LucaSharp-big.jpg";
            ViewBag.Preview = "Experienced Software Engineer with a demonstrated history of working in technology industry. Skilled in .NET Framework, MS SQL Server, C#, AngularJS, and Web API/Services. Strong engineering professional with a Bachelor of Science (B.S.) focused in Computer Information Systems.";
             
            return View(model);
        }
 
        // GET: AboutMeController/Create
        [Authorize]
        public ActionResult EditProfile()
        {
            EditUserViewModel model = new EditUserViewModel();
 
            var owner = _userManager.GetOwner();
            if(owner != null)
                model.EditAboutMe = _mapper.Map<EditAboutMeViewModel>(owner);
 
            var skills = _skillManager.GetSkills();
            if (skills.Count > 0)
                model.EditSkill.Skills = _mapper.Map<List<SkillViewModel>>(skills);
 
            var experiences = _experienceManager.GetExperiences();
            if (experiences.Count > 0)
                model.EditExperience.Experiences = _mapper.Map<List<ExperienceViewModel>>(experiences);
 
            var educations = _educationManager.GetEducations();
            if (educations.Count > 0)
                model.EditEducation.Educations = _mapper.Map<List<EducationViewModel>>(educations);
 
            var achievements = _achievementManager.GetAchievements();
            if (achievements.Count > 0)
                model.EditAchievement.Achievements = achievements;
 
            var projects = _projectManager.GetProjects();
            if (projects.Count > 0)
                model.EditPortfolio.Projects = _mapper.Map<List<ProjectViewModel>>(projects);
 
            return View(model);
        }
 
        private List<ProjectLinkViewModel> ConvertToVM(List<ProjectLink> links)
        {
            List<ProjectLinkViewModel> plList = new List<ProjectLinkViewModel>();
            foreach(var pl in links)
            {
                ProjectLinkViewModel plVM = new ProjectLinkViewModel()
                {
                    Link = pl.Link,
                    LinkName = pl.LinkName
                };
                plList.Add(plVM);
            }
 
            return plList;
        }
 
        // POST: AboutMeController/Create
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SaveAboutMe(EditAboutMeViewModel model)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    _userManager.EditAboutMe(model);
                }
                return PartialView("_EditAboutMe", model);
            }
            catch
            {
                return PartialView("_EditAboutMe", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SaveSkills(SkillsViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _skillManager.EditSkills(model.Skills);
                }
                EditSkillViewModel m = new EditSkillViewModel();
                m.Skills = model.Skills;
                return PartialView("_EditSkill", m);
            }
            catch
            {
                return PartialView("_EditSkill", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddSkill(EditSkillViewModel model)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    SkillViewModel m = new SkillViewModel()
                    {
                        Category = model.Category,
                        Name = model.Name,
                        StartYear = Convert.ToInt32(model.StartYear),
                        Rating = Utility.CalculateSkillRating(model.StartYear)
                    };
                    model.Skills.Add(m);
                }
                return PartialView("_EditSkill", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditSkill", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult RemoveSkill(SkillsViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Skills.RemoveAt(id);
                EditSkillViewModel m = new EditSkillViewModel();
                m.Skills.AddRange(model.Skills);
                return PartialView("_EditSkill", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditSkill", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult UpdateSkill(SkillsViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Skills[id].Rating = Utility.CalculateSkillRating(model.Skills[id].StartYear);
                EditSkillViewModel m = new EditSkillViewModel();
                m.Skills.AddRange(model.Skills);
                return PartialView("_EditSkill", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditSkill", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SaveExperiences(ExperiencesViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _experienceManager.EditExperiences(model.Experiences);
                }
                EditExperienceViewModel m = new EditExperienceViewModel();
                m.Experiences = model.Experiences;
                return PartialView("_EditExperience", m);
            }
            catch
            {
                return PartialView("_EditExperience", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddExp(EditExperienceViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    ExperienceViewModel m = new ExperienceViewModel()
                    {
                        Title = model.Title,
                        Company = model.Company,
                        Location = model.Location,
                        Duration = model.Duration,
                        Items = model.Items
                    };
                    model.Experiences.Add(m);
                }
                return PartialView("_EditExperience", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditExperience", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult RemoveExp(ExperiencesViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Experiences.RemoveAt(id);
                EditExperienceViewModel m = new EditExperienceViewModel();
                m.Experiences.AddRange(model.Experiences);
                return PartialView("_EditExperience", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditExperience", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult UpdateExp(ExperiencesViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                EditExperienceViewModel m = new EditExperienceViewModel();
                m.Experiences.AddRange(model.Experiences);
                return PartialView("_EditExperience", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditExperience", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SaveEducations(EducationsViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _educationManager.EditEducations(model.Educations);
                }
                EditEducationViewModel m = new EditEducationViewModel();
                m.Educations = model.Educations;
                return PartialView("_EditEducation", m);
            }
            catch
            {
                return PartialView("_EditEducation", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddEdu(EditEducationViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    EducationViewModel m = new EducationViewModel()
                    {
                        School = model.School,
                        Major = model.Major,
                        Location = model.Location,
                        Duration = model.Duration,
                        Items = model.Items
                    };
                    model.Educations.Add(m);
                }
                return PartialView("_EditEducation", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditEducation", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult RemoveEdu(EducationsViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Educations.RemoveAt(id);
                EditEducationViewModel m = new EditEducationViewModel();
                m.Educations.AddRange(model.Educations);
                return PartialView("_EditEducation", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditEducation", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult UpdateEdu(EducationsViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                EditEducationViewModel m = new EditEducationViewModel();
                m.Educations.AddRange(model.Educations);
                return PartialView("_EditEducation", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditEducation", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SaveAchievements(AchievementsViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _achievementManager.EditAchievements(model.Achievements);
                }
                EditAchievementViewModel m = new EditAchievementViewModel();
                m.Achievements = model.Achievements;
                return PartialView("_EditAchievement", m);
            }
            catch
            {
                return PartialView("_EditAchievement", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddAchievement(EditAchievementViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    model.Achievements.Add(model.Achievement);
                }
                return PartialView("_EditAchievement", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditAchievement", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult RemoveAchievement(AchievementsViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Achievements.RemoveAt(id);
                EditAchievementViewModel m = new EditAchievementViewModel();
                m.Achievements.AddRange(model.Achievements);
                return PartialView("_EditAchievement", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditAchievement", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult UpdateAchievement(AchievementsViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                EditAchievementViewModel m = new EditAchievementViewModel();
                m.Achievements.AddRange(model.Achievements);
                return PartialView("_EditAchievement", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditAchievement", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddProjectLink(ProjectLinksViewModel model)
        {
            try
            {
                if (model.Links == null)
                    model.Links = new List<ProjectLinkViewModel>();
 
                if (model.Links != null && ModelState.IsValid)
                {
                    model.Links.Add(new ProjectLinkViewModel());
                }
                return PartialView("_EditProjectLinks", model);
            }
            catch
            {
                return PartialView("_EditProjectLinks", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult RemoveProjectLink(ProjectLinksViewModel model, int id)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Links.RemoveAt(id);
                return PartialView("_EditProjectLinks", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditProjectLinks", model);
            }
        }
 
        public ActionResult Login()
        {
            return PartialView("_Login");
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddProject(EditPortfolioViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    ProjectViewModel m = new ProjectViewModel()
                    {
                        Name = model.Name,
                        Description = model.Description,
                        Image = model.FormFile != null ? Utility.ConvertToBase64Image(model.FormFile) : "",
                        ImageFileName = model.FormFile != null ? model.FormFile.FileName : "",
                        Links = model.Links
                    };
                    model.Links = new ProjectLinksViewModel();
                    model.Projects.Add(m);
                }
                return PartialView("_EditPortfolio", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditPortfolio", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult UpdateProject(ProjectsViewModel model, int prjIndex)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
 
                model.Projects[prjIndex].Image = model.Projects[prjIndex].FormFile != null ? Utility.ConvertToBase64Image(model.Projects[prjIndex].FormFile) : model.Projects[prjIndex].Image;
                model.Projects[prjIndex].ImageFileName = model.Projects[prjIndex].FormFile != null ? model.Projects[prjIndex].FormFile.FileName : model.Projects[prjIndex].ImageFileName;
 
                EditPortfolioViewModel m = new EditPortfolioViewModel();
                m.Projects.AddRange(model.Projects);
                return PartialView("_EditPortfolio", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditPortfolio", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult RemoveProject(ProjectsViewModel model, int prjIndex)
        {
            try
            {
                ModelState.Clear(); //need to rebuild model to pass to view
                model.Projects.RemoveAt(prjIndex);
                EditPortfolioViewModel m = new EditPortfolioViewModel();
                m.Projects.AddRange(model.Projects);
                return PartialView("_EditPortfolio", m);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_EditPortfolio", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SavePortfolios(ProjectsViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _projectManager.EditProjects(model.Projects);
                }
                EditPortfolioViewModel m = new EditPortfolioViewModel();
                m.Projects = model.Projects;
                return PartialView("_EditPortfolio", m);
            }
            catch (Exception ex)
            {
                return PartialView("_EditPortfolio", model);
            }
        }
 
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult Login(LoginViewModel model)
        {
            try
            {
                bool valid = false;
 
                if (ModelState.IsValid)
                    valid = _userManager.Login(model);
 
                if (valid)
                {
                    MySession.Set(MyAppContext.Current, "__MyBlogCookie__", Guid.NewGuid().ToString(),
                        model.RememberMe ? DateTime.UtcNow.AddDays(365) : DateTime.UtcNow.AddSeconds(5));
 
                    var userClaims = new List<Claim>()
                    {
                        new Claim(ClaimTypes.Name, "Hoang")
                    };
 
                    var identity = new ClaimsIdentity(userClaims, CookieAuthenticationDefaults.AuthenticationScheme);
                    HttpContext.SignInAsync(new ClaimsPrincipal(identity),
                        new AuthenticationProperties
                        {
                            IsPersistent = model.RememberMe,
                            ExpiresUtc = model.RememberMe ? DateTime.UtcNow.AddDays(365) : DateTime.UtcNow.AddSeconds(5)
                        });
                }
                else
                    ModelState.AddModelError("", "Incorrect Password!");
 
 
                return PartialView("_Login", model);
            }
            catch
            {
                ModelState.AddModelError("", "Something Wrong. Please Try Again!");
                return PartialView("_Login", model);
            }
        }
 
        public ActionResult LogOut()
        {
            HttpContext.SignOutAsync("__MyBlogCookie__");
            MySession.Remove(MyAppContext.Current, "__MyBlogCookie__");
            return RedirectToAction("Index", "Home");
        }
    }
}

BlogController.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Blog.BAL.Helpers;
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
namespace Blog.Web.Controllers
{
    public class BlogController : Controller
    {
        private readonly IBlogManager _blogManager;
        private readonly IUserManager _userManager;
        private readonly ILdObjectManager _ldObjectManager;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IMapper _mapper;
 
        public BlogController(IBlogManager blogerManager, IMapper mapper, IUserManager userManager,
            ILdObjectManager ldObjectManager, IHttpContextAccessor httpContextAccessor)
        {
            _blogManager = blogerManager;
            _userManager = userManager;
            _mapper = mapper;
            _ldObjectManager = ldObjectManager;
            _httpContextAccessor = httpContextAccessor;
        }
 
        // GET: BlogController
        public async Task<IActionResult> Index(string currentFilter, string searchString, string tagFilter, string catFilter, int? pageNumber)
        {
            var model = new BlogViewModel();
 
            if(searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }
 
            ViewData["CurrentFilter"] = searchString;
 
            var posts = _blogManager.GetPosts(null).OrderByDescending(p => p.PublishedDate).ToList();
            var favPosts = posts.OrderByDescending(p => p.LikeCount).Take(5).ToList();
            var popularPosts = posts.OrderByDescending(p => p.ViewCount).Take(5).ToList();
            var aboutMe = _userManager.GetOwner();
 
            model.Categories = posts.Select(p => p.Category).Distinct().ToList();
            model.Categories.Add("All");
 
            if (!string.IsNullOrEmpty(searchString))
            {
                posts = posts.Where(p => p.Title.ToUpper().Contains(searchString.ToUpper())
                                       || p.Category.ToUpper().Contains(searchString.ToUpper())
                                       || p.AuthorName.ToUpper().Contains(searchString.ToUpper())
                                       || p.Series.ToUpper().Contains(searchString.ToUpper())
                                       || p.Preview.ToUpper().Contains(searchString.ToUpper())
                                       || p.Tags.ToUpper().Contains(searchString.ToUpper())).ToList();
            }
 
            if(!string.IsNullOrEmpty(tagFilter))
            {
                ViewData["tagFilter"] = tagFilter;
                posts = posts.Where(p => p.Tags.Contains(tagFilter)).ToList();
            }
 
            ViewBag.LdJsonObject = _ldObjectManager.GetCarouselLdObject(posts);
 
            if (!string.IsNullOrEmpty(catFilter))
            {
                var url = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}";
                var catUrl = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/?catFilter={catFilter}";
 
                ViewBag.LdJsonObject = _ldObjectManager.GetBreadcrumLdObject(new List<string> { "Blog", catFilter },
                                                                             new List<string> { url, catUrl });
                ViewData["catFilter"] = catFilter;
                posts = posts.Where(p => p.Category.ToUpper() == catFilter.ToUpper()).ToList();
            }
 
            int pageSize = 10;
            model.PagedBlog = PaginatedList<PostViewModel>.Create(_mapper.Map<List<PostViewModel>>(posts), pageNumber ?? 1, pageSize);
            model.FavoritePosts = _mapper.Map<List<PostViewModel>>(favPosts);
            model.PopularPosts = _mapper.Map<List<PostViewModel>>(popularPosts);
            model.AboutMe = _mapper.Map<UserViewModel>(aboutMe);
 
            return View(await Task.FromResult(model));
        }
 
        [Authorize]
        public async Task<IActionResult> Dashboard(string sortOrder, string currentFilter, string searchString, int? pageNumber)
        {
            ViewData["TitleSortParm"] = sortOrder == "title" ? "title_desc" : "title";
            ViewData["CategorySortParm"] = sortOrder == "category" ? "category_desc" : "category";
            ViewData["SeriesSortParm"] = sortOrder == "series" ? "series_desc" : "series";
            ViewData["AuthorNameSortParm"] = sortOrder == "authorname" ? "authorname_desc" : "authorname";
            ViewData["DateSortParm"] = sortOrder == "date" ? "date_desc" : "date";
 
            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }
 
            ViewData["CurrentFilter"] = searchString;
 
            List<Post> posts = _blogManager.GetAllPosts(null);
            if (!String.IsNullOrEmpty(searchString))
            {
                posts = posts.Where(p => p.Title.Contains(searchString)
                                       || p.Category.Contains(searchString)
                                       || p.AuthorName.Contains(searchString)
                                       || p.Series.Contains(searchString)).ToList();
            }
 
            switch (sortOrder)
            {
                case "title":
                    posts = posts.OrderBy(p => p.Title).ToList();
                    break;
                case "title_desc":
                    posts = posts.OrderByDescending(p => p.Title).ToList();
                    break;
                case "category":
                    posts = posts.OrderBy(p => p.Category).ToList();
                    break;
                case "category_desc":
                    posts = posts.OrderByDescending(p => p.Category).ToList();
                    break;
                case "series":
                    posts = posts.OrderBy(p => p.Series).ToList();
                    break;
                case "series_desc":
                    posts = posts.OrderByDescending(p => p.Series).ToList();
                    break;
                case "authorname":
                    posts = posts.OrderBy(p => p.AuthorName).ToList();
                    break;
                case "authorname_desc":
                    posts = posts.OrderByDescending(p => p.AuthorName).ToList();
                    break;
                case "date":
                    posts = posts.OrderBy(p => p.PublishedDate).ToList();
                    break;
                case "date_desc":
                    posts = posts.OrderByDescending(p => p.PublishedDate).ToList();
                    break;
                default:
                    posts = posts.OrderByDescending(p => p.PublishedDate).ToList();
                    break;
            }
 
            int pageSize = 5;
            return View(await Task.FromResult(PaginatedList<PostViewModel>.Create(_mapper.Map<List<PostViewModel>>(posts), pageNumber ?? 1, pageSize)));
        }
 
        [Authorize]
        // GET: BlogController/Create
        public ActionResult Create()
        {
            PostViewModel vm = new PostViewModel();
            return View(vm);
        }
 
        [Authorize]
        // POST: BlogController/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(PostViewModel model)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    _blogManager.Create(model);
                    return RedirectToAction(nameof(Dashboard));
                }
                return View(model);
            }
            catch (Exception ex)
            {
                return View(model);
            }
        }
 
        [Authorize]
        // GET: BlogController/Edit/5
        public ActionResult Edit(string url)
        {
            var post = _blogManager.GetPostByUrl(url);
            if (post == null)
                post = _blogManager.GetPostByUrlForArchival(url);
            if (post != null)
            {
                PostViewModel model = _mapper.Map<PostViewModel>(post);
                model.Content = _blogManager.GetContentByUrl(url);
                return View(model);
            }
            return View();
        }
 
        [Authorize]
        // POST: BlogController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(PostViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _blogManager.Edit(model);
                    return RedirectToAction(nameof(Dashboard));
                }
                return View(model);
            }
            catch (Exception ex)
            {
                return View(model);
            }
        }
 
        [Authorize]
        // POST: BlogController/Delete/5
        public ActionResult Archive(string url)
        {
            try
            {
                PostViewModel postVM = _mapper.Map<PostViewModel>(_blogManager.GetPostByUrlForArchival(url));
                postVM.IsActive = !postVM.IsActive;
                _blogManager.Edit(postVM);
 
                return RedirectToAction(nameof(Dashboard));
            }
            catch
            {
                return View();
            }
        }
    }
}

HomeController.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Blog.BAL.Helpers;
using Blog.BAL.Interfaces;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
namespace Blog.Web.Controllers
{
    public class BlogController : Controller
    {
        private readonly IBlogManager _blogManager;
        private readonly IUserManager _userManager;
        private readonly ILdObjectManager _ldObjectManager;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IMapper _mapper;
 
        public BlogController(IBlogManager blogerManager, IMapper mapper, IUserManager userManager,
            ILdObjectManager ldObjectManager, IHttpContextAccessor httpContextAccessor)
        {
            _blogManager = blogerManager;
            _userManager = userManager;
            _mapper = mapper;
            _ldObjectManager = ldObjectManager;
            _httpContextAccessor = httpContextAccessor;
        }
 
        // GET: BlogController
        public async Task<IActionResult> Index(string currentFilter, string searchString, string tagFilter, string catFilter, int? pageNumber)
        {
            var model = new BlogViewModel();
 
            if(searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }
 
            ViewData["CurrentFilter"] = searchString;
 
            var posts = _blogManager.GetPosts(null).OrderByDescending(p => p.PublishedDate).ToList();
            var favPosts = posts.OrderByDescending(p => p.LikeCount).Take(5).ToList();
            var popularPosts = posts.OrderByDescending(p => p.ViewCount).Take(5).ToList();
            var aboutMe = _userManager.GetOwner();
 
            model.Categories = posts.Select(p => p.Category).Distinct().ToList();
            model.Categories.Add("All");
 
            if (!string.IsNullOrEmpty(searchString))
            {
                posts = posts.Where(p => p.Title.ToUpper().Contains(searchString.ToUpper())
                                       || p.Category.ToUpper().Contains(searchString.ToUpper())
                                       || p.AuthorName.ToUpper().Contains(searchString.ToUpper())
                                       || p.Series.ToUpper().Contains(searchString.ToUpper())
                                       || p.Preview.ToUpper().Contains(searchString.ToUpper())
                                       || p.Tags.ToUpper().Contains(searchString.ToUpper())).ToList();
            }
 
            if(!string.IsNullOrEmpty(tagFilter))
            {
                ViewData["tagFilter"] = tagFilter;
                posts = posts.Where(p => p.Tags.Contains(tagFilter)).ToList();
            }
 
            ViewBag.LdJsonObject = _ldObjectManager.GetCarouselLdObject(posts);
 
            if (!string.IsNullOrEmpty(catFilter))
            {
                var url = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}";
                var catUrl = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}/?catFilter={catFilter}";
 
                ViewBag.LdJsonObject = _ldObjectManager.GetBreadcrumLdObject(new List<string> { "Blog", catFilter },
                                                                             new List<string> { url, catUrl });
                ViewData["catFilter"] = catFilter;
                posts = posts.Where(p => p.Category.ToUpper() == catFilter.ToUpper()).ToList();
            }
 
            int pageSize = 10;
            model.PagedBlog = PaginatedList<PostViewModel>.Create(_mapper.Map<List<PostViewModel>>(posts), pageNumber ?? 1, pageSize);
            model.FavoritePosts = _mapper.Map<List<PostViewModel>>(favPosts);
            model.PopularPosts = _mapper.Map<List<PostViewModel>>(popularPosts);
            model.AboutMe = _mapper.Map<UserViewModel>(aboutMe);
 
            return View(await Task.FromResult(model));
        }
 
        [Authorize]
        public async Task<IActionResult> Dashboard(string sortOrder, string currentFilter, string searchString, int? pageNumber)
        {
            ViewData["TitleSortParm"] = sortOrder == "title" ? "title_desc" : "title";
            ViewData["CategorySortParm"] = sortOrder == "category" ? "category_desc" : "category";
            ViewData["SeriesSortParm"] = sortOrder == "series" ? "series_desc" : "series";
            ViewData["AuthorNameSortParm"] = sortOrder == "authorname" ? "authorname_desc" : "authorname";
            ViewData["DateSortParm"] = sortOrder == "date" ? "date_desc" : "date";
 
            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }
 
            ViewData["CurrentFilter"] = searchString;
 
            List<Post> posts = _blogManager.GetAllPosts(null);
            if (!String.IsNullOrEmpty(searchString))
            {
                posts = posts.Where(p => p.Title.Contains(searchString)
                                       || p.Category.Contains(searchString)
                                       || p.AuthorName.Contains(searchString)
                                       || p.Series.Contains(searchString)).ToList();
            }
 
            switch (sortOrder)
            {
                case "title":
                    posts = posts.OrderBy(p => p.Title).ToList();
                    break;
                case "title_desc":
                    posts = posts.OrderByDescending(p => p.Title).ToList();
                    break;
                case "category":
                    posts = posts.OrderBy(p => p.Category).ToList();
                    break;
                case "category_desc":
                    posts = posts.OrderByDescending(p => p.Category).ToList();
                    break;
                case "series":
                    posts = posts.OrderBy(p => p.Series).ToList();
                    break;
                case "series_desc":
                    posts = posts.OrderByDescending(p => p.Series).ToList();
                    break;
                case "authorname":
                    posts = posts.OrderBy(p => p.AuthorName).ToList();
                    break;
                case "authorname_desc":
                    posts = posts.OrderByDescending(p => p.AuthorName).ToList();
                    break;
                case "date":
                    posts = posts.OrderBy(p => p.PublishedDate).ToList();
                    break;
                case "date_desc":
                    posts = posts.OrderByDescending(p => p.PublishedDate).ToList();
                    break;
                default:
                    posts = posts.OrderByDescending(p => p.PublishedDate).ToList();
                    break;
            }
 
            int pageSize = 5;
            return View(await Task.FromResult(PaginatedList<PostViewModel>.Create(_mapper.Map<List<PostViewModel>>(posts), pageNumber ?? 1, pageSize)));
        }
 
        [Authorize]
        // GET: BlogController/Create
        public ActionResult Create()
        {
            PostViewModel vm = new PostViewModel();
            return View(vm);
        }
 
        [Authorize]
        // POST: BlogController/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(PostViewModel model)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    _blogManager.Create(model);
                    return RedirectToAction(nameof(Dashboard));
                }
                return View(model);
            }
            catch (Exception ex)
            {
                return View(model);
            }
        }
 
        [Authorize]
        // GET: BlogController/Edit/5
        public ActionResult Edit(string url)
        {
            var post = _blogManager.GetPostByUrl(url);
            if (post == null)
                post = _blogManager.GetPostByUrlForArchival(url);
            if (post != null)
            {
                PostViewModel model = _mapper.Map<PostViewModel>(post);
                model.Content = _blogManager.GetContentByUrl(url);
                return View(model);
            }
            return View();
        }
 
        [Authorize]
        // POST: BlogController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(PostViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    _blogManager.Edit(model);
                    return RedirectToAction(nameof(Dashboard));
                }
                return View(model);
            }
            catch (Exception ex)
            {
                return View(model);
            }
        }
 
        [Authorize]
        // POST: BlogController/Delete/5
        public ActionResult Archive(string url)
        {
            try
            {
                PostViewModel postVM = _mapper.Map<PostViewModel>(_blogManager.GetPostByUrlForArchival(url));
                postVM.IsActive = !postVM.IsActive;
                _blogManager.Edit(postVM);
 
                return RedirectToAction(nameof(Dashboard));
            }
            catch
            {
                return View();
            }
        }
    }
}

SitemapController.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using AutoMapper;
using Blog.BAL.Interfaces;
using Blog.DAL.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
namespace Blog.Web.Controllers
{
    public class SitemapController : Controller
    {
        private readonly IBlogManager _blogManager;
        private readonly IMapper _mapper;
        private readonly IHttpContextAccessor _httpContextAccessor;
 
        public SitemapController(IBlogManager blogerManager, IMapper mapper, IHttpContextAccessor httpContextAccessor)
        {
            _blogManager = blogerManager;
            _mapper = mapper;
            _httpContextAccessor = httpContextAccessor;
        }
 
        public IActionResult Index()
        {
            var baseUrl = $"https://{_httpContextAccessor.HttpContext.Request.Host.Value}";
 
            var posts = _blogManager.GetPosts(null);
 
            var siteMapBuilder = new SitemapBuilder();
            siteMapBuilder.AddUrl(baseUrl, modified: DateTime.UtcNow, changeFrequency: ChangeFrequency.Yearly, priority: 0.5);
            siteMapBuilder.AddUrl(baseUrl + "/AboutMe", modified: DateTime.UtcNow, changeFrequency: ChangeFrequency.Yearly, priority: 0.6);
            siteMapBuilder.AddUrl(baseUrl + "/Blog", modified: DateTime.UtcNow, changeFrequency: ChangeFrequency.Weekly, priority: 0.8);
 
            foreach (var cat in posts.Select(p => p.Category).Distinct().ToList())
            {
                siteMapBuilder.AddUrl(baseUrl + "/Blog?catFilter=" + HttpUtility.UrlEncode(cat), modified: DateTime.UtcNow, changeFrequency: ChangeFrequency.Weekly, priority: 0.8);
            }
 
            foreach (var post in posts)
            {
                siteMapBuilder.AddUrl(baseUrl + "/?url=" + HttpUtility.UrlEncode(post.Content), modified: post.UpdatedOn, changeFrequency: ChangeFrequency.Weekly, priority: 1.0);
                siteMapBuilder.AddUrl(baseUrl + "/amp/?url=" + HttpUtility.UrlEncode(post.Content), modified: post.UpdatedOn, changeFrequency: ChangeFrequency.Weekly, priority: 1.0);
            }
 
            string xml = siteMapBuilder.ToString();
            return Content(xml, "text/xml");
        }
    }
}

2. In Infrastructures folder, create below classes

AutoMapping.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using AutoMapper;
using Blog.BAL.Models;
using Blog.DAL.Entities;
using System.Linq;
using System.Web;
 
public class AutoMapping : Profile
{
    public AutoMapping()
    {
        CreateMap<User, UserViewModel>();
        CreateMap<User, EditAboutMeViewModel>();
        CreateMap<Skill, SkillViewModel>();
        CreateMap<Experience, ExperienceViewModel>();
        CreateMap<Education, EducationViewModel>();
        CreateMap<Project, ProjectViewModel>();
        CreateMap<ProjectLink, ProjectLinkViewModel>();
        CreateMap<ProjectLinks, ProjectLinksViewModel>();
        CreateMap<Post, PostViewModel>()
            .ForMember(vm => vm.TagsString, m => m.MapFrom(p => p.Tags))
            .AfterMap((dto, jobDto) => jobDto.Tags = dto.Tags.Split(',').ToList());
    }
}<br>

PaginatedList.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
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
 
namespace Blog.Web.Infrastructures
{
    public class PaginatedList<T> : List<T>
    {
        public int PageIndex { get; private set; }
        public int TotalPages { get; private set; }
 
        public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);
 
            this.AddRange(items);
        }
 
        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }
 
        public bool HasNextPage
        {
            get
            {
                return (PageIndex < TotalPages);
            }
        }
 
        public static PaginatedList<T> Create(List<T> source, int pageIndex, int pageSize)
        {
            var count = source.Count();
            var items = source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}

3. In Views folder:

a. In AboutMe folder:

_EditAboutMe.cshtml

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
@model Blog.BAL.Models.EditAboutMeViewModel
 
<div id="abtMeContainer">
    <form asp-action="SaveAboutMe" id="formAboutMe" enctype="multipart/form-data">
        <input name="IsAboutMeValid" type="hidden" value="@ViewData.ModelState.IsValid.ToString()" />
        <div class="row">
            <div class="col-6">
                <div class="form-group">
                    <label asp-for="FormFile" class="control-label"></label>
                    <div class="custom-file mb-3">
                        @*<input asp-for="EditAboutMe.FormFile" type="file"
                               class="custom-file-input" id="customFile" name="filename">
                        <label class="custom-file-label" for="customFile">Choose file</label>*@
                        <input asp-for="FormFile" type="file" id="fileProfileImage">
                    </div>
                    <span asp-validation-for="FormFile" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Name" class="control-label"></label>
                    <input asp-for="Name" class="form-control" />
                    <span asp-validation-for="Name" class="text-danger"></span>
                </div>
            </div>
            <div class="col-6">
                <div class="form-group">
                    <label asp-for="Summary" class="control-label"></label>
                    <textarea class="form-control" asp-for="Summary" rows="5"></textarea>
                    <span asp-validation-for="Summary" class="text-danger"></span>
                </div>
            </div>
        </div>
        <br />
        <div class="row">
            <div class="col-12">
                <div class="form-group text-center">
                    <input type="submit" value="Save" class="btn btn-primary" id="btnSaveAboutMe" />
                </div>
            </div>
        </div>
    </form>
</div>

_EditAchievement.cshtml

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
@model Blog.BAL.Models.EditAchievementViewModel
 
<div id="achievementContainer">
    <form id="formAddAchievement" asp-action="AddAchievement">
        <input name="IsAchievementValid" type="hidden" value="@ViewData.ModelState.IsValid.ToString()" />
        <div class="row row-eq-height" id="mainAddAchievementFrm">
            <div class="col-10">
                <div class="form-group">
                    <label asp-for="Achievement" class="control-label"></label>
                    <div class="input-group">
                        <input type="text" class="form-control" asp-for="Achievement" />
                    </div>
                    <span asp-validation-for="Achievement" class="text-danger"></span>
                </div>
            </div>
            <div class="col-2">
                <label class="control-label">&nbsp;</label>
                <div class="form-group text-center">
                    <button class="btn btn-outline-secondary" type="submit" id="btnAddAchievement">Add</button>
                </div>
            </div>
        </div>
        <div id="divAchievements">
            @if (Model.Achievements.Count > 0)
            {
                <table class="table table-striped" id="tblAchievements">
                    <thead>
                        <tr>
                            <th scope="col" style="width: 90%;">Name</th>
                            <th scope="col" style="width: 10%;">Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        @for (var i = 0; i < Model.Achievements.Count(); i++)
                        {
                            <tr>
                                <td>
                                    <div class="collapse show [email protected]">
                                        <span>@Model.Achievements[i]</span>
                                        <input type="hidden" id="achievementIndex" class="form-control" value="@i" />
                                    </div>
 
                                    <div class="collapse [email protected]">
                                        <input type="text" id="[email protected]" class="form-control" asp-for="@Model.Achievements[i]"
                                               onkeydown="return event.keyCode != 13;" />
                                    </div>
                                </td>
                                <td class="align-middle">
                                    <div class="collapse show [email protected]">
                                        <a class="edit" title="Edit" data-toggle="collapse"
                                           data-target="[email protected]" aria-expanded="false">
                                            <i class="fas fa-edit"></i>
                                        </a>
                                        <a class="delete" title="Delete" id="btnRemoveAchievement" data-toggle="tooltip">
                                            <i class="far fa-trash-alt"></i>
                                        </a>
                                        <br />
                                    </div>
                                    <div class="collapse [email protected]">
                                        <a class="update" title="Update" id="btnUpdateAchievement" data-toggle="tooltip">
                                            <i class="far fa-check-circle"></i>
                                        </a>
                                        <a class="cancel" title="Cancel" data-toggle="collapse"
                                           data-target="[email protected]" aria-expanded="false">
                                            <i class="far fa-times-circle"></i>
                                        </a>
                                    </div>
                                </td>
                            </tr>
                        }
                    </tbody>
                </table>
                <div class="row">
                    <div class="col-12">
                        <div class="form-group text-center">
                            <input type="submit" value="Save" class="btn btn-primary" id="btnSaveAchievements" />
                        </div>
                    </div>
                </div>
            }
        </div>
    </form>
</div>

_EditEducation.cshtml

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
@model Blog.BAL.Models.EditEducationViewModel
 
<div id="eduContainer">
    <form id="formAddEdu" asp-action="AddEdu">
        <input name="IsEduValid" type="hidden" value="@ViewData.ModelState.IsValid.ToString()" />
        <div id="mainAddEduFrm">
            <div class="row row-eq-height">
                <div class="col-6">
                    <div class="form-group">
                        <label asp-for="School" class="control-label"></label>
                        <div class="input-group">
                            <input type="text" class="form-control" asp-for="School">
                        </div>
                        <span asp-validation-for="School" class="text-danger"></span>
                    </div>
                </div>
                <div class="col-6">
                    <div class="form-group">
                        <label asp-for="Major" class="control-label"></label>
                        <div class="input-group">
                            <input type="text" class="form-control" asp-for="Major">
                        </div>
                        <span asp-validation-for="Major" class="text-danger"></span>
                    </div>
                </div>
            </div>
            <div class="row row-eq-height">
                <div class="col-6">
                    <div class="form-group">
                        <label asp-for="Location" class="control-label"></label>
                        <div class="input-group">
                            <input type="text" class="form-control" asp-for="Location">
                        </div>
                        <span asp-validation-for="Location" class="text-danger"></span>
                    </div>
                </div>
                <div class="col-6">
                    <div class="form-group">
                        <label asp-for="Duration" class="control-label"></label>
                        <div class="input-group">
                            <input type="text" class="form-control" asp-for="Duration">
                        </div>
                        <span asp-validation-for="Duration" class="text-danger"></span>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-12">
                    <div class="form-group">
                        <label asp-for="Items" class="control-label"></label>
                        <textarea class="form-control" asp-for="Items" rows="5"></textarea>
                        <span asp-validation-for="Items" class="text-danger"></span>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-12 text-center">
                    <div class="form-group text-center">
                        <button class="btn btn-outline-secondary" type="submit" id="btnAddEdu">Add</button>
                    </div>
                </div>
            </div>
 
        </div>
        <div id="divEdus">
            @if (Model.Educations.Count > 0)
            {
                <table class="table table-striped" id="tblEducations">
                    <thead>
                        <tr>
                            <th scope="col" style="width: 90%;">Educations</th>
                            <th scope="col" style="width: 10%;">Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        @for (var i = 0; i < Model.Educations.Count(); i++)
                        {
                            <tr>
                                <td>
                                    <div class="row row-eq-height">
                                        <div class="col-6">
                                            <div class="form-group collapse show [email protected]">
                                                <label asp-for="@Model.Educations[i].School" class="control-label font-weight-bold"></label>
                                                <div class="input-group pl-1">
                                                    <span>@Model.Educations[i].School</span>
                                                    <input type="hidden" id="eduIndex" class="form-control" value="@i" />
                                                </div>
                                            </div>
                                            <div class="form-group collapse [email protected]">
                                                <label asp-for="@Model.Educations[i].School" class="control-label"></label>
                                                <div class="input-group">
                                                    <input type="text" class="form-control" asp-for="@Model.Educations[i].School">
                                                </div>
                                                <span asp-validation-for="@Model.Educations[i].School" class="text-danger"></span>
                                            </div>
                                        </div>
                                        <div class="col-6">
                                            <div class="form-group collapse show [email protected]">
                                                <label asp-for="@Model.Educations[i].Major" class="control-label font-weight-bold"></label>
                                                <div class="input-group pl-1">
                                                    <span>@Model.Educations[i].Major</span>
                                                </div>
                                            </div>
                                            <div class="form-group collapse [email protected]">
                                                <label asp-for="@Model.Educations[i].Major" class="control-label"></label>
                                                <