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 @@ +
+
+

Latest Changes

+
+ +
\ 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 - - - - -
-
-

Latest Changes

-
- -
- - - - - - 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