diff --git a/OurUmbraco.Site/Views/Partials/Community/Scripts.cshtml b/OurUmbraco.Site/Views/Partials/Community/Scripts.cshtml
index 60303a44..5a254870 100644
--- a/OurUmbraco.Site/Views/Partials/Community/Scripts.cshtml
+++ b/OurUmbraco.Site/Views/Partials/Community/Scripts.cshtml
@@ -1,9 +1,14 @@
\ No newline at end of file
diff --git a/OurUmbraco.Site/Views/Partials/Home/GitHubContributors.cshtml b/OurUmbraco.Site/Views/Partials/Home/GitHubContributors.cshtml
new file mode 100644
index 00000000..046f20d8
--- /dev/null
+++ b/OurUmbraco.Site/Views/Partials/Home/GitHubContributors.cshtml
@@ -0,0 +1,35 @@
+@model OurUmbraco.Community.Models.GitHubContributorsModel
+
+@if (Model.Contributors.Any())
+{
+ foreach (var contributor in Model.Contributors)
+ {
+ var author = contributor.Author;
+ if (author != null)
+ {
+
+
+

+
+ @contributor.TotalCommits
+
+
+ Additions
+ @contributor.TotalAdditions
+
+ Deletions
+ @contributor.TotalDeletions
+
+
+
+ }
+ }
+
+}
+
+else
+{
+
Could not load recent GitHub contributors.
+}
\ No newline at end of file
diff --git a/OurUmbraco.Site/config/githubhq.txt b/OurUmbraco.Site/config/githubhq.txt
new file mode 100644
index 00000000..63a5edc7
--- /dev/null
+++ b/OurUmbraco.Site/config/githubhq.txt
@@ -0,0 +1,16 @@
+Aaen
+clausjensen
+emilwangaa
+hartvig
+hemraker
+madsrasmussen
+mikkelhm
+nul800sebastiaan
+perploug
+Shazwazza
+simonbusborg
+sitereactor
+sofietoft
+umbracoci
+warrenbuckley
+zpqrtbnk
\ No newline at end of file
diff --git a/OurUmbraco/Community/Controllers/GitHubContributorController.cs b/OurUmbraco/Community/Controllers/GitHubContributorController.cs
new file mode 100644
index 00000000..4f6d86ea
--- /dev/null
+++ b/OurUmbraco/Community/Controllers/GitHubContributorController.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Web.Mvc;
+using OurUmbraco.Community.Models;
+using OurUmbraco.Our.Api;
+using RestSharp;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
+using Umbraco.Web.Mvc;
+
+namespace OurUmbraco.Community.Controllers
+{
+ public class GitHubContributorController : SurfaceController
+ {
+ ///
+ /// Repositories to include in the combination of contributions
+ ///
+ private readonly string[] Repositories =
+ {
+ "Umbraco-CMS",
+ "UmbracoDocs",
+ "OurUmbraco",
+ "Umbraco.Deploy.Contrib",
+ "Umbraco.Courier.Contrib",
+ "Umbraco.Deploy.ValueConnectors"
+ };
+
+ ///
+ /// Gets data for all GitHub contributors for all listed Umbraco repositories,
+ /// excluding the GitHub IDs of the HQ contributors from the text file list
+ ///
+ ///
+ public ActionResult GitHubGetContributorsResult()
+ {
+ var model = new GitHubContributorsModel();
+ try
+ {
+ string configPath = Server.MapPath("~/config/githubhq.txt");
+ if (!System.IO.File.Exists(configPath))
+ {
+ LogHelper.Debug
("Config file was not found: " + configPath);
+ return PartialView("~/Views/Partials/Home/GitHubContributors.cshtml", model);
+ }
+
+ string[] login = System.IO.File.ReadAllLines(configPath).Where(x => x.Trim() != "").Distinct().ToArray();
+ var contributors = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem>("UmbracoGitHubContributors",
+ () =>
+ {
+ var githubController = new GitHubController();
+ var gitHubContributors = new List();
+ foreach (var repo in Repositories)
+ {
+ var response = githubController.GetAllRepoContributors(repo);
+ if (response.StatusCode == HttpStatusCode.OK &&
+ response.ResponseStatus == ResponseStatus.Completed)
+ {
+ gitHubContributors.AddRange(response.Data);
+ }
+ else
+ {
+ LogHelper.Warn(string.Format("Invalid HTTP response for repository {0}", repo));
+ }
+ }
+
+ var filteredContributors = gitHubContributors
+ .Where(g => !login.Contains(g.Author.Login))
+ .GroupBy(g => g.Author.Id)
+ .OrderByDescending(c => c.Sum(g => g.Total));
+
+ List temp = new List();
+
+ foreach (var group in filteredContributors)
+ {
+ temp.Add(new GitHubGlobalContributorModel(group));
+ }
+
+ return temp;
+
+ }, TimeSpan.FromDays(1));
+
+
+ model.Contributors = contributors;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.Error("Could not get GitHub Contributors", ex);
+ }
+
+ return PartialView("~/Views/Partials/Home/GitHubContributors.cshtml", model);
+ }
+ }
+}
diff --git a/OurUmbraco/Community/Controllers/GitHubGlobalContributorModel.cs b/OurUmbraco/Community/Controllers/GitHubGlobalContributorModel.cs
new file mode 100644
index 00000000..a5766ed3
--- /dev/null
+++ b/OurUmbraco/Community/Controllers/GitHubGlobalContributorModel.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Linq;
+using OurUmbraco.Community.Models;
+
+namespace OurUmbraco.Community.Controllers
+{
+ public class GitHubGlobalContributorModel
+ {
+ public List Items { get; set; }
+
+ public int Id
+ {
+ get { return Author.Id; }
+ }
+
+ public int TotalCommits
+ {
+ get { return Items.Sum(x => x.Total); }
+ }
+
+ public int TotalAdditions
+ {
+ get { return Items.Sum(x => x.TotalAdditions); }
+ }
+
+ public int TotalDeletions
+ {
+ get { return Items.Sum(x => x.TotalDeletions); }
+ }
+
+ public Author Author
+ {
+ get { return Items.First().Author; }
+ }
+
+ public GitHubGlobalContributorModel(IEnumerable items)
+ {
+ Items = items.ToList();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/OurUmbraco/Community/Models/GitHubContributorsModel.cs b/OurUmbraco/Community/Models/GitHubContributorsModel.cs
new file mode 100644
index 00000000..592a5524
--- /dev/null
+++ b/OurUmbraco/Community/Models/GitHubContributorsModel.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using OurUmbraco.Community.Controllers;
+
+namespace OurUmbraco.Community.Models
+{
+ public class GitHubContributorsModel : IGitHubContributorsModel
+ {
+ public List Contributors { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/OurUmbraco/Community/Models/GithubContributorModel.cs b/OurUmbraco/Community/Models/GithubContributorModel.cs
new file mode 100644
index 00000000..3180b06e
--- /dev/null
+++ b/OurUmbraco/Community/Models/GithubContributorModel.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace OurUmbraco.Community.Models
+{
+ [DataContract]
+ public class GitHubContributorModel : IGitHubContributorModel
+ {
+
+ private List _weeks;
+
+ public int Total { get; set; }
+ public int TotalAdditions { get; set; }
+ public int TotalDeletions { get; set; }
+
+ [DataMember(Name = "weeks")]
+ public List Weeks
+ {
+ get { return _weeks; }
+
+ set
+ {
+ _weeks = value;
+
+
+ int totalAdditions = 0;
+ int totalDeletions = 0;
+ foreach (var week in _weeks)
+ {
+ totalDeletions += week.D;
+ totalAdditions += week.A;
+ }
+
+ TotalAdditions = totalAdditions;
+ TotalDeletions = totalDeletions;
+
+ }
+
+ }
+ public Author Author { get; set; }
+ }
+
+ [DataContract]
+ public class Week
+ {
+ ///
+ /// Timestamp
+ ///
+ public int W { get; set; }
+
+ ///
+ /// Additions
+ ///
+ public int A { get; set; }
+
+ ///
+ /// Deletions
+ ///
+ public int D { get; set; }
+
+ ///
+ /// Commits
+ ///
+ public int C { get; set; }
+ }
+
+ [DataContract]
+ public class Author
+ {
+ public string Login { get; set; }
+
+ public int Id { get; set; }
+
+ [DataMember(Name = "avatar_url")]
+ public string AvatarUrl { get; set; }
+
+ [DataMember(Name = "gravatar_id")]
+ public string GravatarId { get; set; }
+
+ public string Url { get; set; }
+
+ [DataMember(Name = "html_url")]
+ public string HtmlUrl { get; set; }
+
+ [DataMember(Name = "followers_url")]
+ public string FollowersUrl { get; set; }
+
+ [DataMember(Name = "following_url")]
+ public string FollowingsUrl { get; set; }
+
+ [DataMember(Name = "gists_url")]
+ public string GistsUrl { get; set; }
+
+ [DataMember(Name = "starred_url")]
+ public string StarredUrl { get; set; }
+
+ [DataMember(Name = "subscriptions_url")]
+ public string SubscriptionsUrl { get; set; }
+
+ [DataMember(Name = "organizations_url")]
+ public string OrganizationsUrl { get; set; }
+
+ [DataMember(Name = "repos_url")]
+ public string ReposUrl { get; set; }
+
+ [DataMember(Name = "events_url")]
+ public string EventsUrl { get; set; }
+
+ [DataMember(Name = "received_events_url")]
+ public string ReceivedEventsUrl { get; set; }
+
+ public string Type { get; set; }
+
+ [DataMember(Name = "site_admin")]
+ public bool SiteAdmin { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/OurUmbraco/Community/Models/IGitHubContributorModel.cs b/OurUmbraco/Community/Models/IGitHubContributorModel.cs
new file mode 100644
index 00000000..39bddcb4
--- /dev/null
+++ b/OurUmbraco/Community/Models/IGitHubContributorModel.cs
@@ -0,0 +1,6 @@
+namespace OurUmbraco.Community.Models
+{
+ public interface IGitHubContributorModel
+ {
+ }
+}
\ No newline at end of file
diff --git a/OurUmbraco/Community/Models/IGitHubContributorsModel.cs b/OurUmbraco/Community/Models/IGitHubContributorsModel.cs
new file mode 100644
index 00000000..02f98006
--- /dev/null
+++ b/OurUmbraco/Community/Models/IGitHubContributorsModel.cs
@@ -0,0 +1,6 @@
+namespace OurUmbraco.Community.Models
+{
+ public interface IGitHubContributorsModel
+ {
+ }
+}
\ No newline at end of file
diff --git a/OurUmbraco/Our/Api/GithubController.cs b/OurUmbraco/Our/Api/GithubController.cs
new file mode 100644
index 00000000..471a27c1
--- /dev/null
+++ b/OurUmbraco/Our/Api/GithubController.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using OurUmbraco.Community.Models;
+using RestSharp;
+
+namespace OurUmbraco.Our.Api
+{
+ public class GitHubController
+ {
+ private const string RepositoryOwner = "Umbraco";
+ private const string GitHubApiClient = "https://api.github.com";
+ private const string UserAgent = "OurUmbraco";
+
+ ///
+ /// Get all contributors from GitHub Umbraco repositories
+ ///
+ ///
+ ///
+ public IRestResponse> GetAllRepoContributors(string repo)
+ {
+ var client = new RestClient(GitHubApiClient);
+ var request = new RestRequest(string.Format("/repos/{0}/{1}/stats/contributors", RepositoryOwner, repo), Method.GET);
+ client.UserAgent = UserAgent;
+ var response = client.Execute>(request);
+ return response;
+ }
+ }
+}
diff --git a/OurUmbraco/OurUmbraco.csproj b/OurUmbraco/OurUmbraco.csproj
index 7e15d8a3..4d155f07 100644
--- a/OurUmbraco/OurUmbraco.csproj
+++ b/OurUmbraco/OurUmbraco.csproj
@@ -403,10 +403,16 @@
+
+
+
+
+
+
@@ -519,6 +525,7 @@
+