diff --git a/app/assets/javascripts/admin/controllers/admin_github_commits_controller.js b/app/assets/javascripts/admin/controllers/admin_github_commits_controller.js
new file mode 100644
index 000000000..0a6bd38ea
--- /dev/null
+++ b/app/assets/javascripts/admin/controllers/admin_github_commits_controller.js
@@ -0,0 +1,13 @@
+/**
+ This controller is for the widget that shows the commits to the discourse repo.
+
+ @class AdminGithubCommitsController
+ @extends Ember.ArrayController
+ @namespace Discourse
+ @module Discourse
+**/
+Discourse.AdminGithubCommitsController = Ember.ArrayController.extend({
+ goToGithub: function() {
+ window.open('https://github.com/discourse/discourse');
+ }
+});
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/models/github_commit.js b/app/assets/javascripts/admin/models/github_commit.js
new file mode 100644
index 000000000..140e4e4ea
--- /dev/null
+++ b/app/assets/javascripts/admin/models/github_commit.js
@@ -0,0 +1,43 @@
+/**
+ A model for a git commit to the discourse repo, fetched from the github.com api.
+
+ @class GithubCommit
+ @extends Discourse.Model
+ @namespace Discourse
+ @module Discourse
+**/
+Discourse.GithubCommit = Discourse.Model.extend({
+ gravatarUrl: function(){
+ if( this.get('author') && this.get('author.gravatar_id') ){
+ return("https://www.gravatar.com/avatar/" + this.get('author.gravatar_id') + ".png?s=38&r=pg&d=identicon");
+ } else {
+ return "https://www.gravatar.com/avatar/b30fff48d257cdd17c4437afac19fd30.png?s=38&r=pg&d=identicon";
+ }
+ }.property("commit"),
+
+ commitUrl: function(){
+ return("https://github.com/discourse/discourse/commit/" + this.get('sha'));
+ }.property("sha"),
+
+ timeAgo: function() {
+ return Date.create(this.get('commit.committer.date')).relative();
+ }.property("commit.committer.date")
+});
+
+Discourse.GithubCommit.reopenClass({
+ findAll: function() {
+ var result;
+ result = Em.A();
+ $.ajax( "https://api.github.com/repos/discourse/discourse/commits?callback=callback", {
+ dataType: 'jsonp',
+ type: 'get',
+ data: { per_page: 10 },
+ success: function(response, textStatus, jqXHR) {
+ response.data.each(function(commit) {
+ result.pushObject( Discourse.GithubCommit.create(commit) );
+ });
+ }
+ });
+ return result;
+ }
+});
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/routes/admin_dashboard_route.js b/app/assets/javascripts/admin/routes/admin_dashboard_route.js
index da31f82d8..bf7571a4b 100644
--- a/app/assets/javascripts/admin/routes/admin_dashboard_route.js
+++ b/app/assets/javascripts/admin/routes/admin_dashboard_route.js
@@ -8,12 +8,9 @@
**/
Discourse.AdminDashboardRoute = Discourse.Route.extend({
setupController: function(c) {
- if( !c.get('versionCheckedAt') || Date.create('12 hours ago') > c.get('versionCheckedAt') ) {
- this.checkVersion(c);
- }
- if( !c.get('reportsCheckedAt') || Date.create('1 hour ago') > c.get('reportsCheckedAt') ) {
- this.fetchReports(c);
- }
+ this.checkVersion(c);
+ this.fetchReports(c);
+ this.fetchGithubCommits(c);
},
renderTemplate: function() {
@@ -21,7 +18,7 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
},
checkVersion: function(c) {
- if( Discourse.SiteSettings.version_checks ) {
+ if( Discourse.SiteSettings.version_checks && (!c.get('versionCheckedAt') || Date.create('12 hours ago') > c.get('versionCheckedAt')) ) {
c.set('versionCheckedAt', new Date());
Discourse.VersionCheck.find().then(function(vc) {
c.set('versionCheck', vc);
@@ -31,11 +28,20 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
},
fetchReports: function(c) {
- // TODO: use one request to get all reports, or maybe one request for all dashboard data including version check.
- c.set('reportsCheckedAt', new Date());
- ['visits', 'signups', 'topics', 'posts'].each(function(reportType){
- c.set(reportType, Discourse.Report.find(reportType));
- });
+ if( !c.get('reportsCheckedAt') || Date.create('1 hour ago') > c.get('reportsCheckedAt') ) {
+ // TODO: use one request to get all reports, or maybe one request for all dashboard data including version check.
+ c.set('reportsCheckedAt', new Date());
+ ['visits', 'signups', 'topics', 'posts'].each(function(reportType){
+ c.set(reportType, Discourse.Report.find(reportType));
+ });
+ }
+ },
+
+ fetchGithubCommits: function(c) {
+ if( !c.get('commitsCheckedAt') || Date.create('1 hour ago') > c.get('commitsCheckedAt') ) {
+ c.set('commitsCheckedAt', new Date());
+ c.set('githubCommits', Discourse.GithubCommit.findAll());
+ }
}
});
diff --git a/app/assets/javascripts/admin/templates/commits.js.handlebars b/app/assets/javascripts/admin/templates/commits.js.handlebars
new file mode 100644
index 000000000..d381ae9ec
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/commits.js.handlebars
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/templates/dashboard.js.handlebars b/app/assets/javascripts/admin/templates/dashboard.js.handlebars
index 987f1dc02..e97df94c2 100644
--- a/app/assets/javascripts/admin/templates/dashboard.js.handlebars
+++ b/app/assets/javascripts/admin/templates/dashboard.js.handlebars
@@ -37,7 +37,7 @@
-
+ {{ render admin_github_commits githubCommits }}
diff --git a/app/assets/javascripts/admin/views/admin_github_commits_view.js b/app/assets/javascripts/admin/views/admin_github_commits_view.js
new file mode 100644
index 000000000..2f5511bca
--- /dev/null
+++ b/app/assets/javascripts/admin/views/admin_github_commits_view.js
@@ -0,0 +1,11 @@
+/**
+ A view for showing commits to the discourse repo.
+
+ @class AdminGithubCommitsView
+ @extends Discourse.View
+ @namespace Discourse
+ @module Discourse
+**/
+Discourse.AdminGithubCommitsView = Discourse.View.extend({
+ templateName: 'admin/templates/commits'
+});
\ No newline at end of file
diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss
index ce927f4c2..3e47626f3 100644
--- a/app/assets/stylesheets/admin/admin_base.scss
+++ b/app/assets/stylesheets/admin/admin_base.scss
@@ -1,6 +1,7 @@
// these are the styles associated with the Discourse admin section
@import "foundation/variables";
@import "foundation/mixins";
+@import "foundation/helpers";
.admin-content {
margin-bottom: 50px;
@@ -318,4 +319,107 @@ table {
text-align: center;
}
}
+}
+
+
+.commits-widget {
+ border: solid 1px #ccc;
+ width: 500px;
+ height: 160px;
+
+ ul, li {
+ margin: 0;
+ padding: 0;
+ }
+ ul {
+ list-style: none;
+ }
+ a {
+ color: #222;
+ text-decoration: none
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+
+ .header {
+ color: #222;
+ font-weight: bold;
+ height: 30px;
+ border-bottom: solid 1px #ccc;
+
+ background-color:#e1e1e1;
+ background-image:-moz-linear-gradient(top, #f1f1f1, #e1e1e1);
+ background-image:-ms-linear-gradient(top, #f1f1f1, #e1e1e1);
+ background-image:-o-linear-gradient(top, #f1f1f1, #e1e1e1);
+ background-image:-webkit-gradient(linear, left top, left bottom, from(#f1f1f1), to(#e1e1e1));
+ background-image:-webkit-linear-gradient(top, #f1f1f1, #e1e1e1);
+ background-image:linear-gradient(center top, #f1f1f1 0%, #e1e1e1 100%);
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorStr='#f1f1f1', EndColorStr='#e1e1e1');
+ cursor: pointer;
+
+ h1 {
+ font-size: 18px;
+ margin: 5px 0 0 8px;
+ display: inline-block;
+ line-height: 1.0em;
+ }
+ }
+
+ .header:hover h1 {
+ text-decoration: underline;
+ }
+
+ .commits-list {
+ height: 129px;
+ overflow-y:auto;
+
+ li {
+ @extend .clearfix;
+ line-height: 1.0em;
+ padding: 6px 8px;
+ border-bottom: solid 1px #ccc;
+ background-color:#eee;
+ background-image:-moz-linear-gradient(top, #fafafa, #eee);
+ background-image:-ms-linear-gradient(top, #fafafa, #eee);
+ background-image:-o-linear-gradient(top, #fafafa, #eee);
+ background-image:-webkit-gradient(linear, left top, left bottom, from(#fafafa), to(#eee));
+ background-image:-webkit-linear-gradient(top, #fafafa, #eee);
+ background-image:linear-gradient(center top, #fafafa 0%, #eee 100%);
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorStr='#f1f1f1', EndColorStr='#e1e1e1');
+
+ .left {
+ float: left;
+ }
+ .right {
+ margin-left: 52px;
+ }
+
+ img {
+ margin-top: 2px;
+ border: solid 1px #ccc;
+ padding: 2px;
+ background-color: white;
+ }
+
+ .commit-message {
+ color: #222;
+ font-size: 12px;
+ font-weight: bold;
+ }
+
+ .commit-meta {
+ color: #555;
+ font-size: 12px;
+ }
+
+ .committer-name {
+ font-weight: bold;
+ color: #333;
+ }
+ }
+ li:last-child {
+ border: none;
+ }
+ }
}
\ No newline at end of file
diff --git a/public/commits-widget/index.html b/public/commits-widget/index.html
deleted file mode 100644
index 629d04dbe..000000000
--- a/public/commits-widget/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
- Discourse.org Latest Commits Widget
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/commits-widget/javascripts/commits-widget.js b/public/commits-widget/javascripts/commits-widget.js
deleted file mode 100644
index 80d55c570..000000000
--- a/public/commits-widget/javascripts/commits-widget.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Parameters:
- * limit: (integer) How many commits to render, starting with the most recent commit
- * width: (integer) Width of the widget
- * height: (integer) Height of the widget
- * heading: (string) Text in the header of the widget
- */
-$(function(){
- var $commitsList = $('.commits-list');
- var keyValuePairs = window.location.href.slice(window.location.href.indexOf("?") + 1).split("&");
- var x, params = {};
- $.each(keyValuePairs, function(i, keyValue){
- x = keyValue.split('=');
- params[x[0]] = x[1];
- });
-
- if( params.width ) {
- $('.widget-container').css('width', params.width + 'px');
- }
- if( params.height ) {
- $('.widget-container').css('height', params.height + 'px');
- $('.widget-container .commits-list').css('height', (params.height - 31) + 'px');
- }
- if( params.heading ) {
- $('.widget-container h1').text( decodeURIComponent(params.heading) );
- }
-
- $('.widget-container .header').click(function(){
- window.open('https://github.com/discourse/discourse');
- });
-
- $.ajax( "https://api.github.com/repos/discourse/discourse/commits?callback=callback", {
- dataType: 'jsonp',
- type: 'get',
- data: {
- per_page: params.limit || 10
- },
- success: function(response, textStatus, jqXHR) {
- var data = response.data;
- $.each(data, function(i, commit){
- var $li = $(' ').appendTo( $commitsList );
- if( commit.sha && commit.commit && commit.commit.message && commit.commit.author && commit.commit.committer && commit.commit.committer.date ) {
- if( commit.author && commit.author.gravatar_id ) {
- $('').appendTo( $li );
- } else {
- $('').appendTo( $li );
- }
- $right = $('
').appendTo( $li );
- $('' + commit.commit.message + ' ').appendTo( $right );
- $('by ' + commit.commit.author.name + ' - ' + $.timeago(commit.commit.committer.date) + ' ').appendTo( $right );
- $('
').appendTo( $li );
- } else {
- // Render nothing. Or render a message:
- // $('
').appendTo( $li );
- // $right = $('
').appendTo( $li );
- // $('this commit cannot be rendered ').appendTo( $right );
- // $('
').appendTo( $li );
- }
- });
- }
- });
-});
diff --git a/public/commits-widget/javascripts/jquery.timeago.js b/public/commits-widget/javascripts/jquery.timeago.js
deleted file mode 100644
index 5eb3fbfbe..000000000
--- a/public/commits-widget/javascripts/jquery.timeago.js
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * Timeago is a jQuery plugin that makes it easy to support automatically
- * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
- *
- * @name timeago
- * @version 1.0.2
- * @requires jQuery v1.2.3+
- * @author Ryan McGeary
- * @license MIT License - http://www.opensource.org/licenses/mit-license.php
- *
- * For usage and examples, visit:
- * http://timeago.yarp.com/
- *
- * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
- */
-
-(function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], factory);
- } else {
- // Browser globals
- factory(jQuery);
- }
-}(function ($) {
- $.timeago = function(timestamp) {
- if (timestamp instanceof Date) {
- return inWords(timestamp);
- } else if (typeof timestamp === "string") {
- return inWords($.timeago.parse(timestamp));
- } else if (typeof timestamp === "number") {
- return inWords(new Date(timestamp));
- } else {
- return inWords($.timeago.datetime(timestamp));
- }
- };
- var $t = $.timeago;
-
- $.extend($.timeago, {
- settings: {
- refreshMillis: 60000,
- allowFuture: false,
- strings: {
- prefixAgo: null,
- prefixFromNow: null,
- suffixAgo: "ago",
- suffixFromNow: "from now",
- seconds: "less than a minute",
- minute: "about a minute",
- minutes: "%d minutes",
- hour: "about an hour",
- hours: "about %d hours",
- day: "a day",
- days: "%d days",
- month: "about a month",
- months: "%d months",
- year: "about a year",
- years: "%d years",
- wordSeparator: " ",
- numbers: []
- }
- },
- inWords: function(distanceMillis) {
- var $l = this.settings.strings;
- var prefix = $l.prefixAgo;
- var suffix = $l.suffixAgo;
- if (this.settings.allowFuture) {
- if (distanceMillis < 0) {
- prefix = $l.prefixFromNow;
- suffix = $l.suffixFromNow;
- }
- }
-
- var seconds = Math.abs(distanceMillis) / 1000;
- var minutes = seconds / 60;
- var hours = minutes / 60;
- var days = hours / 24;
- var years = days / 365;
-
- function substitute(stringOrFunction, number) {
- var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
- var value = ($l.numbers && $l.numbers[number]) || number;
- return string.replace(/%d/i, value);
- }
-
- var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
- seconds < 90 && substitute($l.minute, 1) ||
- minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
- minutes < 90 && substitute($l.hour, 1) ||
- hours < 24 && substitute($l.hours, Math.round(hours)) ||
- hours < 42 && substitute($l.day, 1) ||
- days < 30 && substitute($l.days, Math.round(days)) ||
- days < 45 && substitute($l.month, 1) ||
- days < 365 && substitute($l.months, Math.round(days / 30)) ||
- years < 1.5 && substitute($l.year, 1) ||
- substitute($l.years, Math.round(years));
-
- var separator = $l.wordSeparator || "";
- if ($l.wordSeparator === undefined) { separator = " "; }
- return $.trim([prefix, words, suffix].join(separator));
- },
- parse: function(iso8601) {
- var s = $.trim(iso8601);
- s = s.replace(/\.\d+/,""); // remove milliseconds
- s = s.replace(/-/,"/").replace(/-/,"/");
- s = s.replace(/T/," ").replace(/Z/," UTC");
- s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
- return new Date(s);
- },
- datetime: function(elem) {
- var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
- return $t.parse(iso8601);
- },
- isTime: function(elem) {
- // jQuery's `is()` doesn't play well with HTML5 in IE
- return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
- }
- });
-
- $.fn.timeago = function() {
- var self = this;
- self.each(refresh);
-
- var $s = $t.settings;
- if ($s.refreshMillis > 0) {
- setInterval(function() { self.each(refresh); }, $s.refreshMillis);
- }
- return self;
- };
-
- function refresh() {
- var data = prepareData(this);
- if (!isNaN(data.datetime)) {
- $(this).text(inWords(data.datetime));
- }
- return this;
- }
-
- function prepareData(element) {
- element = $(element);
- if (!element.data("timeago")) {
- element.data("timeago", { datetime: $t.datetime(element) });
- var text = $.trim(element.text());
- if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
- element.attr("title", text);
- }
- }
- return element.data("timeago");
- }
-
- function inWords(date) {
- return $t.inWords(distance(date));
- }
-
- function distance(date) {
- return (new Date().getTime() - date.getTime());
- }
-
- // fix for IE6 suckage
- document.createElement("abbr");
- document.createElement("time");
-}));
\ No newline at end of file
diff --git a/public/commits-widget/stylesheets/stylesheet.css b/public/commits-widget/stylesheets/stylesheet.css
deleted file mode 100644
index c3cf082e4..000000000
--- a/public/commits-widget/stylesheets/stylesheet.css
+++ /dev/null
@@ -1,162 +0,0 @@
-/*******************************************************************************
-MeyerWeb Reset
-*******************************************************************************/
-
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font: inherit;
- vertical-align: baseline;
-}
-
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
- display: block;
-}
-
-ol, ul {
- list-style: none;
-}
-
-blockquote, q {
-}
-
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-a:focus {
- outline: none;
-}
-
-.clearfix:before, .clearfix:after {
- display: table;
- content: " ";
-}
-.clearfix:after {
- clear: both;
-}
-
-/*******************************************************************************
-Theme Styles
-*******************************************************************************/
-
-.widget-container {
- border: solid 1px #ccc;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- width: 500px;
- height: 200px;
-}
-
-.widget-container .header {
- color: #222;
- font-weight: bold;
- height: 30px;
- border-bottom: solid 1px #ccc;
-
- background-color:#e1e1e1;
- background-image:-moz-linear-gradient(top, #f1f1f1, #e1e1e1);
- background-image:-ms-linear-gradient(top, #f1f1f1, #e1e1e1);
- background-image:-o-linear-gradient(top, #f1f1f1, #e1e1e1);
- background-image:-webkit-gradient(linear, left top, left bottom, from(#f1f1f1), to(#e1e1e1));
- background-image:-webkit-linear-gradient(top, #f1f1f1, #e1e1e1);
- background-image:linear-gradient(center top, #f1f1f1 0%, #e1e1e1 100%);
- filter:progid:DXImageTransform.Microsoft.gradient(startColorStr='#f1f1f1', EndColorStr='#e1e1e1');
- cursor: pointer;
-}
-
-.widget-container .header:hover h1 {
- text-decoration: underline;
-}
-
-.widget-container .header h1 {
- font-size: 18px;
- margin: 3px 0 0 8px;
- display: inline-block;
-}
-
-.widget-container .header .github-icon {
- width: 22px;
- height: 22px;
- margin: 3px 0 0 5px;
- vertical-align: top;
- background: url(../images/github-icon.png) no-repeat 0 0;
- opacity: .65;
- display: inline-block;
-}
-
-.widget-container .commits-list {
- height: 169px;
- overflow-y:auto;
- line-height: 0.85em;
-}
-
-.widget-container .commits-list li {
- padding: 6px 8px;
- border-bottom: solid 1px #ccc;
- background-color:#eee;
- background-image:-moz-linear-gradient(top, #fafafa, #eee);
- background-image:-ms-linear-gradient(top, #fafafa, #eee);
- background-image:-o-linear-gradient(top, #fafafa, #eee);
- background-image:-webkit-gradient(linear, left top, left bottom, from(#fafafa), to(#eee));
- background-image:-webkit-linear-gradient(top, #fafafa, #eee);
- background-image:linear-gradient(center top, #fafafa 0%, #eee 100%);
- filter:progid:DXImageTransform.Microsoft.gradient(startColorStr='#f1f1f1', EndColorStr='#e1e1e1');
-}
-.widget-container .commits-list li:last-child {
- border: none;
-}
-
-.widget-container .commits-list li .left {
- float: left;
-}
-
-.widget-container .commits-list li .right {
- margin-left: 52px;
-}
-
-.widget-container .commits-list li img {
- margin-top: 2px;
- border: solid 1px #ccc;
- padding: 2px;
- background-color: white;
-}
-
-.widget-container .commits-list li .commit-message {
- color: #222;
- font-size: 12px;
- font-weight: bold;
-}
-
-.widget-container .commits-list li .commit-meta {
- color: #555;
- font-size: 12px;
-}
-
-.widget-container .commits-list li .committer-name {
- font-weight: bold;
- color: #333;
-}
-
-.widget-container a {
- color: #222;
- text-decoration: none
-}
-.widget-container a:hover {
- text-decoration: underline;
-}
\ No newline at end of file