mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Merge remote-tracking branch 'upstream/master' into mywork
This commit is contained in:
commit
3a6a4c628d
2
Gemfile
2
Gemfile
@ -12,7 +12,7 @@ gem 'redcarpet', require: false
|
|||||||
gem 'activerecord-postgres-hstore'
|
gem 'activerecord-postgres-hstore'
|
||||||
gem 'acts_as_paranoid'
|
gem 'acts_as_paranoid'
|
||||||
gem 'active_attr' # until we get ActiveModel::Model with Rails 4
|
gem 'active_attr' # until we get ActiveModel::Model with Rails 4
|
||||||
gem 'airbrake', '3.1.2' # errbit is broken with 3.1.3 for now
|
gem 'airbrake', '3.1.2', require: false # errbit is broken with 3.1.3 for now
|
||||||
gem 'clockwork', require: false
|
gem 'clockwork', require: false
|
||||||
gem 'em-redis'
|
gem 'em-redis'
|
||||||
gem 'eventmachine'
|
gem 'eventmachine'
|
||||||
|
@ -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');
|
||||||
|
}
|
||||||
|
});
|
43
app/assets/javascripts/admin/models/github_commit.js
Normal file
43
app/assets/javascripts/admin/models/github_commit.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
});
|
@ -8,12 +8,9 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.AdminDashboardRoute = Discourse.Route.extend({
|
Discourse.AdminDashboardRoute = Discourse.Route.extend({
|
||||||
setupController: function(c) {
|
setupController: function(c) {
|
||||||
if( !c.get('versionCheckedAt') || Date.create('12 hours ago') > c.get('versionCheckedAt') ) {
|
this.checkVersion(c);
|
||||||
this.checkVersion(c);
|
this.fetchReports(c);
|
||||||
}
|
this.fetchGithubCommits(c);
|
||||||
if( !c.get('reportsCheckedAt') || Date.create('1 hour ago') > c.get('reportsCheckedAt') ) {
|
|
||||||
this.fetchReports(c);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
@ -21,7 +18,7 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
checkVersion: function(c) {
|
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());
|
c.set('versionCheckedAt', new Date());
|
||||||
Discourse.VersionCheck.find().then(function(vc) {
|
Discourse.VersionCheck.find().then(function(vc) {
|
||||||
c.set('versionCheck', vc);
|
c.set('versionCheck', vc);
|
||||||
@ -31,11 +28,20 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fetchReports: function(c) {
|
fetchReports: function(c) {
|
||||||
// TODO: use one request to get all reports, or maybe one request for all dashboard data including version check.
|
if( !c.get('reportsCheckedAt') || Date.create('1 hour ago') > c.get('reportsCheckedAt') ) {
|
||||||
c.set('reportsCheckedAt', new Date());
|
// TODO: use one request to get all reports, or maybe one request for all dashboard data including version check.
|
||||||
['visits', 'signups', 'topics', 'posts'].each(function(reportType){
|
c.set('reportsCheckedAt', new Date());
|
||||||
c.set(reportType, Discourse.Report.find(reportType));
|
['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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
18
app/assets/javascripts/admin/templates/commits.js.handlebars
Normal file
18
app/assets/javascripts/admin/templates/commits.js.handlebars
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<div class="commits-widget">
|
||||||
|
<div class="header" {{action "goToGithub"}}>
|
||||||
|
<h1>Latest Changes</h4>
|
||||||
|
</div>
|
||||||
|
<ul class="commits-list">
|
||||||
|
{{#each controller}}
|
||||||
|
<li>
|
||||||
|
<div class="left">
|
||||||
|
<img {{bindAttr src="gravatarUrl"}}>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<span class="commit-message"><a {{bindAttr href="commitUrl"}} target="_blank">{{ commit.message }}</a></span><br/>
|
||||||
|
<span class="commit-meta">by <span class="committer-name">{{ commit.author.name }}</span> - <span class="commit-time">{{ timeAgo }}</span></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="version-check-right">
|
<div class="version-check-right">
|
||||||
<iframe src="/commits-widget/index.html?limit=10&height=160" allowtransparency="true" frameborder="0" scrolling="no" width="502px" height="162px"></iframe>
|
{{ render admin_github_commits githubCommits }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='clearfix'></div>
|
<div class='clearfix'></div>
|
||||||
|
@ -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'
|
||||||
|
});
|
@ -24,6 +24,8 @@
|
|||||||
//= require ./discourse/helpers/i18n_helpers
|
//= require ./discourse/helpers/i18n_helpers
|
||||||
//= require ./discourse
|
//= require ./discourse
|
||||||
|
|
||||||
|
//= require ./locales/date_locales.js
|
||||||
|
|
||||||
// Stuff we need to load first
|
// Stuff we need to load first
|
||||||
//= require_tree ./discourse/mixins
|
//= require_tree ./discourse/mixins
|
||||||
//= require ./discourse/views/view
|
//= require ./discourse/views/view
|
||||||
|
@ -29,50 +29,44 @@ Discourse.Development = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
after: function(data, owner, args) {
|
after: function(data, owner, args) {
|
||||||
var ary, f, n, v, _ref;
|
|
||||||
|
if (typeof console === "undefined") return;
|
||||||
|
if (console === null) return;
|
||||||
|
|
||||||
|
var f, n, v;
|
||||||
if (owner && data.time > 10) {
|
if (owner && data.time > 10) {
|
||||||
|
|
||||||
f = function(name, data) {
|
f = function(name, data) {
|
||||||
if (data && data.count) return name + " - " + data.count + " calls " + ((data.time + 0.0).toFixed(2)) + "ms";
|
if (data && data.count) return name + " - " + data.count + " calls " + ((data.time + 0.0).toFixed(2)) + "ms";
|
||||||
};
|
};
|
||||||
|
|
||||||
if (console && console.group) {
|
if (console.group) {
|
||||||
console.group(f(name, data));
|
console.group(f(name, data));
|
||||||
} else {
|
} else {
|
||||||
console.log("");
|
console.log("");
|
||||||
console.log(f(name, data));
|
console.log(f(name, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
ary = [];
|
var ary = [];
|
||||||
_ref = window.probes;
|
for (n in window.probes) {
|
||||||
for (n in _ref) {
|
v = window.probes[n];
|
||||||
v = _ref[n];
|
if (n === name || v.time < 1) continue;
|
||||||
if (n === name || v.time < 1) {
|
ary.push({ k: n, v: v });
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ary.push({
|
|
||||||
k: n,
|
|
||||||
v: v
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
ary.sortBy(function(item) {
|
ary.sortBy(function(item) {
|
||||||
if (item.v && item.v.time) {
|
if (item.v && item.v.time) return -item.v.time;
|
||||||
return -item.v.time;
|
return 0;
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}).each(function(item) {
|
}).each(function(item) {
|
||||||
var output = f("" + item.k, item.v);
|
var output = f("" + item.k, item.v);
|
||||||
if (output) {
|
if (output) {
|
||||||
return console.log(output);
|
console.log(output);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (typeof console !== "undefined" && console !== null) {
|
|
||||||
if (typeof console.groupEnd === "function") {
|
if (console.group) {
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return window.probes.clear();
|
window.probes.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
* someFunction = window.probes.measure(someFunction, {
|
* someFunction = window.probes.measure(someFunction, {
|
||||||
* name: "somename" // or function(args) { return "name"; },
|
* name: "somename" // or function(args) { return "name"; },
|
||||||
* before: function(data, owner, args) {
|
* before: function(data, owner, args) {
|
||||||
* // if owner is true, we are not in a recursive function call.
|
* // if owner is true, we are not in a recursive function call.
|
||||||
* //
|
* //
|
||||||
* // data contains the bucker of data already measuer
|
* // data contains the bucker of data already measuer
|
||||||
* // data.count >= 0
|
* // data.count >= 0
|
||||||
* // data.time is the total time measured till now
|
* // data.time is the total time measured till now
|
||||||
* //
|
* //
|
||||||
* // arguments contains the original arguments sent to the function
|
* // arguments contains the original arguments sent to the function
|
||||||
* },
|
* },
|
||||||
* after: function(data, owner, args) {
|
* after: function(data, owner, args) {
|
||||||
* // same format as before
|
* // same format as before
|
||||||
@ -22,9 +22,9 @@
|
|||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* // minimal
|
* // minimal
|
||||||
* someFunction = window.probes.measure(someFunction, "someFunction");
|
* someFunction = window.probes.measure(someFunction, "someFunction");
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
(function(){
|
(function(){
|
||||||
var measure, clear;
|
var measure, clear;
|
||||||
@ -92,8 +92,7 @@
|
|||||||
start = now();
|
start = now();
|
||||||
callStart = start;
|
callStart = start;
|
||||||
}
|
}
|
||||||
else if(after)
|
else if(after) {
|
||||||
{
|
|
||||||
callStart = now();
|
callStart = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/*global humaneDate:true */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Breaks up a long string
|
Breaks up a long string
|
||||||
|
|
||||||
@ -162,7 +160,7 @@ Handlebars.registerHelper('avatar', function(user, options) {
|
|||||||
Handlebars.registerHelper('unboundDate', function(property, options) {
|
Handlebars.registerHelper('unboundDate', function(property, options) {
|
||||||
var dt;
|
var dt;
|
||||||
dt = new Date(Ember.Handlebars.get(this, property, options));
|
dt = new Date(Ember.Handlebars.get(this, property, options));
|
||||||
return dt.format("{d} {Mon}, {yyyy} {hh}:{mm}");
|
return dt.format("long");
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,9 +174,9 @@ Handlebars.registerHelper('editDate', function(property, options) {
|
|||||||
dt = Date.create(Ember.Handlebars.get(this, property, options));
|
dt = Date.create(Ember.Handlebars.get(this, property, options));
|
||||||
yesterday = new Date() - (60 * 60 * 24 * 1000);
|
yesterday = new Date() - (60 * 60 * 24 * 1000);
|
||||||
if (yesterday > dt.getTime()) {
|
if (yesterday > dt.getTime()) {
|
||||||
return dt.format("{d} {Mon}, {yyyy} {hh}:{mm}");
|
return dt.format("long");
|
||||||
} else {
|
} else {
|
||||||
return humaneDate(dt);
|
return dt.relative();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,7 +213,7 @@ Handlebars.registerHelper('number', function(property, options) {
|
|||||||
@for Handlebars
|
@for Handlebars
|
||||||
**/
|
**/
|
||||||
Handlebars.registerHelper('date', function(property, options) {
|
Handlebars.registerHelper('date', function(property, options) {
|
||||||
var displayDate, dt, fiveDaysAgo, fullReadable, humanized, leaveAgo, val;
|
var displayDate, dt, fiveDaysAgo, oneMinuteAgo, fullReadable, humanized, leaveAgo, val;
|
||||||
if (property.hash) {
|
if (property.hash) {
|
||||||
if (property.hash.leaveAgo) {
|
if (property.hash.leaveAgo) {
|
||||||
leaveAgo = property.hash.leaveAgo === "true";
|
leaveAgo = property.hash.leaveAgo === "true";
|
||||||
@ -229,23 +227,26 @@ Handlebars.registerHelper('date', function(property, options) {
|
|||||||
return new Handlebars.SafeString("—");
|
return new Handlebars.SafeString("—");
|
||||||
}
|
}
|
||||||
dt = new Date(val);
|
dt = new Date(val);
|
||||||
fullReadable = dt.format("{d} {Mon}, {yyyy} {hh}:{mm}");
|
fullReadable = dt.format("long");
|
||||||
displayDate = "";
|
displayDate = "";
|
||||||
fiveDaysAgo = (new Date()) - 432000000;
|
fiveDaysAgo = (new Date()) - 432000000;
|
||||||
if (fiveDaysAgo > (dt.getTime())) {
|
oneMinuteAgo = (new Date()) - 60000;
|
||||||
|
if (oneMinuteAgo <= dt.getTime() && dt.getTime() <= (new Date())) {
|
||||||
|
displayDate = Em.String.i18n("now");
|
||||||
|
} else if (fiveDaysAgo > (dt.getTime())) {
|
||||||
if ((new Date()).getFullYear() !== dt.getFullYear()) {
|
if ((new Date()).getFullYear() !== dt.getFullYear()) {
|
||||||
displayDate = dt.format("{d} {Mon} '{yy}");
|
displayDate = dt.format("short");
|
||||||
} else {
|
} else {
|
||||||
displayDate = dt.format("{d} {Mon}");
|
displayDate = dt.format("short_no_year");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
humanized = humaneDate(dt);
|
humanized = dt.relative();
|
||||||
if (!humanized) {
|
if (!humanized) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
displayDate = humanized;
|
displayDate = humanized;
|
||||||
if (!leaveAgo) {
|
if (!leaveAgo) {
|
||||||
displayDate = displayDate.replace(' ago', '');
|
displayDate = (dt.millisecondsAgo()).duration();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Handlebars.SafeString("<span class='date' title='" + fullReadable + "'>" + displayDate + "</span>");
|
return new Handlebars.SafeString("<span class='date' title='" + fullReadable + "'>" + displayDate + "</span>");
|
||||||
@ -267,4 +268,4 @@ Handlebars.registerHelper('personalizedName', function(property, options) {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return Em.String.i18n('you');
|
return Em.String.i18n('you');
|
||||||
});
|
});
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td><label>Must be unique, no spaces. People can mention you as @username.</label></td>
|
<td><label>{{i18n user.username.instructions}}</label></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{{#if view.passwordRequired}}
|
{{#if view.passwordRequired}}
|
||||||
@ -50,7 +50,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<tr class="password-confirmation">
|
<tr class="password-confirmation">
|
||||||
<td><label for='new-account-password-confirmation'>Password Again</label></td>
|
<td><label for='new-account-password-confirmation'>{{i18n user.password_confirmation.title}}</label></td>
|
||||||
<td>
|
<td>
|
||||||
{{view Ember.TextField valueBinding="view.accountPasswordConfirm" type="password" id="new-account-password-confirmation"}}
|
{{view Ember.TextField valueBinding="view.accountPasswordConfirm" type="password" id="new-account-password-confirmation"}}
|
||||||
{{view Ember.TextField valueBinding="view.accountChallenge" id="new-account-challenge"}}
|
{{view Ember.TextField valueBinding="view.accountChallenge" id="new-account-challenge"}}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
{{#linkTo "preferences.username" class="btn pad-left"}}{{i18n user.change_username.action}}{{/linkTo}}
|
{{#linkTo "preferences.username" class="btn pad-left"}}{{i18n user.change_username.action}}{{/linkTo}}
|
||||||
</div>
|
</div>
|
||||||
<div class='instructions'>
|
<div class='instructions'>
|
||||||
{{{i18n user.username.instructions username="content.username"}}}
|
{{{i18n user.username.short_instructions username="content.username"}}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='instructions'>
|
<div class='instructions'>
|
||||||
{{i18n user.name.instructions}}
|
{{i18n user.name.instructions}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{i18n user.password.title}}</label>
|
<label class="control-label">{{i18n user.password.title}}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<a href="#" {{action changePassword target="controller"}} class='btn'>{{i18n user.change_password}}</a> {{controller.passwordProgress}}
|
<a href="#" {{action changePassword target="controller"}} class='btn'>{{i18n user.change_password}}</a> {{controller.passwordProgress}}
|
||||||
</div>
|
</div>
|
||||||
@ -52,32 +52,32 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='instructions'>
|
<div class='instructions'>
|
||||||
{{{i18n user.avatar.instructions}}} {{content.email}}
|
{{{i18n user.avatar.instructions}}} {{content.email}}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label">{{i18n user.bio}}</label>
|
|
||||||
<div class="controls">
|
|
||||||
{{view Discourse.PagedownEditor valueBinding="content.bio_raw"}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
|
<label class="control-label">{{i18n user.bio}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
{{view Discourse.PagedownEditor valueBinding="content.bio_raw"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
<label class="control-label">{{i18n user.website}}</label>
|
<label class="control-label">{{i18n user.website}}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{view Ember.TextField valueBinding="content.website" classNames="input-xxlarge"}}
|
{{view Ember.TextField valueBinding="content.website" classNames="input-xxlarge"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{i18n user.email_settings}}</label>
|
<label class="control-label">{{i18n user.email_settings}}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label>{{view Ember.Checkbox checkedBinding="content.email_digests"}}
|
<label>{{view Ember.Checkbox checkedBinding="content.email_digests"}}
|
||||||
{{i18n user.email_digests.title}}</label>
|
{{i18n user.email_digests.title}}</label>
|
||||||
|
|
||||||
{{#if content.email_digests}}
|
{{#if content.email_digests}}
|
||||||
<div class='control-indent'>
|
<div class='control-indent'>
|
||||||
{{view Discourse.ComboboxView valueAttribute="value" contentBinding="controller.digestFrequencies" valueBinding="content.digest_after_days"}}
|
{{view Discourse.ComboboxView valueAttribute="value" contentBinding="controller.digestFrequencies" valueBinding="content.digest_after_days"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<label>{{view Ember.Checkbox checkedBinding="content.email_private_messages"}}
|
<label>{{view Ember.Checkbox checkedBinding="content.email_private_messages"}}
|
||||||
@ -87,19 +87,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='instructions'>
|
<div class='instructions'>
|
||||||
{{i18n user.email.frequency}}
|
{{i18n user.email.frequency}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group other">
|
<div class="control-group other">
|
||||||
<label class="control-label">{{i18n user.other_settings}}</label>
|
<label class="control-label">{{i18n user.other_settings}}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label>{{i18n user.auto_track_topics}}</label>
|
<label>{{i18n user.auto_track_topics}}</label>
|
||||||
{{view Discourse.ComboboxView valueAttribute="value" contentBinding="controller.autoTrackDurations" valueBinding="content.auto_track_topics_after_msecs"}}
|
{{view Discourse.ComboboxView valueAttribute="value" contentBinding="controller.autoTrackDurations" valueBinding="content.auto_track_topics_after_msecs"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label>{{i18n user.new_topic_duration.label}}</label>
|
<label>{{i18n user.new_topic_duration.label}}</label>
|
||||||
{{view Discourse.ComboboxView valueAttribute="value" contentBinding="controller.considerNewTopicOptions" valueBinding="content.new_topic_duration_minutes"}}
|
{{view Discourse.ComboboxView valueAttribute="value" contentBinding="controller.considerNewTopicOptions" valueBinding="content.new_topic_duration_minutes"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ Discourse.TopicListItemView = Discourse.View.extend({
|
|||||||
}
|
}
|
||||||
// highlight new topics that have been loaded from the server or the one we just created
|
// highlight new topics that have been loaded from the server or the one we just created
|
||||||
else if (this.get('content.highlight')) {
|
else if (this.get('content.highlight')) {
|
||||||
|
this.set('content.highlight', false);
|
||||||
this.highlight();
|
this.highlight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,8 +276,19 @@ Discourse.CreateAccountView = Discourse.ModalBodyView.extend({
|
|||||||
_this.set('formSubmitted', false);
|
_this.set('formSubmitted', false);
|
||||||
return _this.flash(Em.String.i18n('create_account.failed'), 'error');
|
return _this.flash(Em.String.i18n('create_account.failed'), 'error');
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement: function(e) {
|
||||||
|
// allows the submission the form when pressing 'ENTER' on *any* text input field
|
||||||
|
// but only when the submit button is enabled
|
||||||
|
var _this = this;
|
||||||
|
return Em.run.next(function() {
|
||||||
|
return $("input[type='text']").keydown(function(e) {
|
||||||
|
if (_this.get('submitDisabled') === false && e.keyCode === 13) {
|
||||||
|
return _this.createAccount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,9 +32,15 @@ Discourse.PagedownEditor = Ember.ContainerView.extend({
|
|||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
var $wmdInput = $('#wmd-input');
|
var $wmdInput = $('#wmd-input');
|
||||||
$wmdInput.data('init', true);
|
$wmdInput.data('init', true);
|
||||||
this.editor = Discourse.Markdown.createEditor();
|
this.set('editor', Discourse.Markdown.createEditor());
|
||||||
return this.editor.run();
|
return this.get('editor').run();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
observeValue: (function() {
|
||||||
|
var editor = this.get('editor');
|
||||||
|
if (!editor) return;
|
||||||
|
Ember.run.next(null, function() { editor.refreshPreview(); });
|
||||||
|
}).observes('value')
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
134
app/assets/javascripts/external/humane.js
vendored
134
app/assets/javascripts/external/humane.js
vendored
@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* Javascript Humane Dates
|
|
||||||
* Copyright (c) 2008 Dean Landolt (deanlandolt.com)
|
|
||||||
* Re-write by Zach Leatherman (zachleat.com)
|
|
||||||
*
|
|
||||||
* Adopted from the John Resig's pretty.js
|
|
||||||
* at http://ejohn.org/blog/javascript-pretty-date
|
|
||||||
* and henrah's proposed modification
|
|
||||||
* at http://ejohn.org/blog/javascript-pretty-date/#comment-297458
|
|
||||||
*
|
|
||||||
* Licensed under the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function humaneDate(date, compareTo){
|
|
||||||
|
|
||||||
if(!date) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lang = {
|
|
||||||
ago: 'ago',
|
|
||||||
from: '',
|
|
||||||
now: 'just now',
|
|
||||||
minute: 'minute',
|
|
||||||
minutes: 'minutes',
|
|
||||||
hour: 'hour',
|
|
||||||
hours: 'hours',
|
|
||||||
day: 'day',
|
|
||||||
days: 'days',
|
|
||||||
week: 'week',
|
|
||||||
weeks: 'weeks',
|
|
||||||
month: 'month',
|
|
||||||
months: 'months',
|
|
||||||
year: 'year',
|
|
||||||
years: 'years'
|
|
||||||
},
|
|
||||||
formats = [
|
|
||||||
[60, lang.now],
|
|
||||||
[3600, lang.minute, lang.minutes, 60], // 60 minutes, 1 minute
|
|
||||||
[86400, lang.hour, lang.hours, 3600], // 24 hours, 1 hour
|
|
||||||
[604800, lang.day, lang.days, 86400], // 7 days, 1 day
|
|
||||||
[2628000, lang.week, lang.weeks, 604800], // ~1 month, 1 week
|
|
||||||
[31536000, lang.month, lang.months, 2628000], // 1 year, ~1 month
|
|
||||||
[Infinity, lang.year, lang.years, 31536000] // Infinity, 1 year
|
|
||||||
],
|
|
||||||
isString = typeof date == 'string',
|
|
||||||
date = isString ?
|
|
||||||
new Date(('' + date).replace(/-/g,"/").replace(/[TZ]/g," ")) :
|
|
||||||
date,
|
|
||||||
compareTo = compareTo || new Date,
|
|
||||||
seconds = (compareTo - date +
|
|
||||||
(compareTo.getTimezoneOffset() -
|
|
||||||
// if we received a GMT time from a string, doesn't include time zone bias
|
|
||||||
// if we got a date object, the time zone is built in, we need to remove it.
|
|
||||||
(isString ? 0 : date.getTimezoneOffset())
|
|
||||||
) * 60000
|
|
||||||
) / 1000,
|
|
||||||
token;
|
|
||||||
|
|
||||||
if(seconds < 0) {
|
|
||||||
seconds = Math.abs(seconds);
|
|
||||||
token = lang.from ? ' ' + lang.from : '';
|
|
||||||
} else {
|
|
||||||
token = lang.ago ? ' ' + lang.ago : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 0 seconds && < 60 seconds Now
|
|
||||||
* 60 seconds 1 Minute
|
|
||||||
* > 60 seconds && < 60 minutes X Minutes
|
|
||||||
* 60 minutes 1 Hour
|
|
||||||
* > 60 minutes && < 24 hours X Hours
|
|
||||||
* 24 hours 1 Day
|
|
||||||
* > 24 hours && < 7 days X Days
|
|
||||||
* 7 days 1 Week
|
|
||||||
* > 7 days && < ~ 1 Month X Weeks
|
|
||||||
* ~ 1 Month 1 Month
|
|
||||||
* > ~ 1 Month && < 1 Year X Months
|
|
||||||
* 1 Year 1 Year
|
|
||||||
* > 1 Year X Years
|
|
||||||
*
|
|
||||||
* Single units are +10%. 1 Year shows first at 1 Year + 10%
|
|
||||||
*/
|
|
||||||
|
|
||||||
function normalize(val, single)
|
|
||||||
{
|
|
||||||
var margin = 0.1;
|
|
||||||
if(val >= single && val <= single * (1+margin)) {
|
|
||||||
return single;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var i = 0, format = formats[0]; formats[i]; format = formats[++i]) {
|
|
||||||
if(seconds < format[0]) {
|
|
||||||
if(i === 0) {
|
|
||||||
// Now
|
|
||||||
return format[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = Math.ceil(normalize(seconds, format[3]) / (format[3]));
|
|
||||||
return val +
|
|
||||||
' ' +
|
|
||||||
(val != 1 ? format[2] : format[1]) +
|
|
||||||
(i > 0 ? token : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if(typeof jQuery != 'undefined') {
|
|
||||||
jQuery.fn.humaneDates = function(options)
|
|
||||||
{
|
|
||||||
var settings = jQuery.extend({
|
|
||||||
'lowercase': false
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
return this.each(function()
|
|
||||||
{
|
|
||||||
var $t = jQuery(this),
|
|
||||||
date = $t.attr('datetime') || $t.attr('title');
|
|
||||||
|
|
||||||
date = humaneDate(date);
|
|
||||||
|
|
||||||
if(date && settings['lowercase']) {
|
|
||||||
date = date.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(date && $t.html() != date) {
|
|
||||||
// don't modify the dom if we don't have to
|
|
||||||
$t.html(date);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
36
app/assets/javascripts/locales/date_locales.js
Normal file
36
app/assets/javascripts/locales/date_locales.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// fix EN locale
|
||||||
|
Date.getLocale('en').short_no_year = '{d} {Mon}';
|
||||||
|
|
||||||
|
// create CS locale
|
||||||
|
Date.addLocale('cs', {
|
||||||
|
'plural': true,
|
||||||
|
'capitalizeUnit': false,
|
||||||
|
'months': 'ledna,února,března,dubna,května,června,července,srpna,září,října,listopadu,prosince',
|
||||||
|
'weekdays': 'neděle,pondělí,úterý,středa,čtvrtek,pátek,sobota',
|
||||||
|
'units': 'milisekund:a|y||ou|ami,sekund:a|y||ou|ami,minut:a|y||ou|ami,hodin:a|y||ou|ami,den|dny|dnů|dnem|dny,týden|týdny|týdnů|týdnem|týdny,měsíc:|e|ů|em|emi,rok|roky|let|rokem|lety',
|
||||||
|
'short': '{d}. {month} {yyyy}',
|
||||||
|
'short_no_year': '{d}. {month}',
|
||||||
|
'long': '{d}. {month} {yyyy} {H}:{mm}',
|
||||||
|
'full': '{weekday} {d}. {month} {yyyy} {H}:{mm}:{ss}',
|
||||||
|
'relative': function(num, unit, ms, format) {
|
||||||
|
var numberWithUnit, last = num.toString().slice(-1);
|
||||||
|
var mult;
|
||||||
|
if (format === 'past' || format === 'future') {
|
||||||
|
if (num === 1) mult = 3;
|
||||||
|
else mult = 4;
|
||||||
|
} else {
|
||||||
|
if (num === 1) mult = 0;
|
||||||
|
else if (num >= 2 && num <= 4) mult = 1;
|
||||||
|
else mult = 2;
|
||||||
|
}
|
||||||
|
numberWithUnit = num + ' ' + this.units[(mult * 8) + unit];
|
||||||
|
switch(format) {
|
||||||
|
case 'duration': return numberWithUnit;
|
||||||
|
case 'past': return 'před ' + numberWithUnit;
|
||||||
|
case 'future': return 'za ' + numberWithUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set the current date locale
|
||||||
|
Date.setLocale(I18n.locale);
|
@ -1,6 +1,7 @@
|
|||||||
// these are the styles associated with the Discourse admin section
|
// these are the styles associated with the Discourse admin section
|
||||||
@import "foundation/variables";
|
@import "foundation/variables";
|
||||||
@import "foundation/mixins";
|
@import "foundation/mixins";
|
||||||
|
@import "foundation/helpers";
|
||||||
|
|
||||||
.admin-content {
|
.admin-content {
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
@ -318,4 +319,107 @@ table {
|
|||||||
text-align: center;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -103,11 +103,11 @@ class TopicViewSerializer < ApplicationSerializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_reply_as_new_topic
|
def can_reply_as_new_topic
|
||||||
scope.can_reply_as_new_topic?(object.topic)
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_can_reply_as_new_topic?
|
def include_can_reply_as_new_topic?
|
||||||
scope.can_create?(Post, object.topic)
|
scope.can_reply_as_new_topic?(object.topic)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_create_post
|
def can_create_post
|
||||||
|
@ -88,11 +88,9 @@ module Discourse
|
|||||||
# Our templates shouldn't start with 'discourse/templates'
|
# Our templates shouldn't start with 'discourse/templates'
|
||||||
config.handlebars.templates_root = 'discourse/templates'
|
config.handlebars.templates_root = 'discourse/templates'
|
||||||
|
|
||||||
|
require 'discourse_redis'
|
||||||
# Use redis for our cache
|
# Use redis for our cache
|
||||||
redis_config = YAML::load(File.open("#{Rails.root}/config/redis.yml"))[Rails.env]
|
config.cache_store = DiscourseRedis.new_redis_store
|
||||||
redis_store = ActiveSupport::Cache::RedisStore.new "redis://#{redis_config['host']}:#{redis_config['port']}/#{redis_config['cache_db']}"
|
|
||||||
redis_store.options[:namespace] = -> { DiscourseRedis.namespace }
|
|
||||||
config.cache_store = redis_store
|
|
||||||
|
|
||||||
# Test with rack::cache disabled. Nginx does this for us
|
# Test with rack::cache disabled. Nginx does this for us
|
||||||
config.action_dispatch.rack_cache = nil
|
config.action_dispatch.rack_cache = nil
|
||||||
|
@ -33,12 +33,6 @@ Discourse::Application.configure do
|
|||||||
config.ember.ember_location = "#{Rails.root}/app/assets/javascripts/external/ember.js"
|
config.ember.ember_location = "#{Rails.root}/app/assets/javascripts/external/ember.js"
|
||||||
config.handlebars.precompile = false
|
config.handlebars.precompile = false
|
||||||
|
|
||||||
# a bit hacky but works
|
|
||||||
config.after_initialize do
|
|
||||||
config.middleware.delete Airbrake::UserInformer
|
|
||||||
config.middleware.delete Airbrake::Rack
|
|
||||||
end
|
|
||||||
|
|
||||||
config.action_mailer.delivery_method = :smtp
|
config.action_mailer.delivery_method = :smtp
|
||||||
config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
|
config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
|
||||||
config.action_mailer.raise_delivery_errors = true
|
config.action_mailer.raise_delivery_errors = true
|
||||||
|
20
config/initializers/01-redis.rb
Normal file
20
config/initializers/01-redis.rb
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
require "#{Rails.root}/lib/discourse_redis"
|
||||||
|
|
||||||
|
$redis = DiscourseRedis.new
|
||||||
|
|
||||||
|
if Rails.env.development? && !ENV['DO_NOT_FLUSH_REDIS']
|
||||||
|
puts "Flushing redis (development mode)"
|
||||||
|
$redis.flushall
|
||||||
|
end
|
||||||
|
|
||||||
|
if defined?(PhusionPassenger)
|
||||||
|
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
||||||
|
if forked
|
||||||
|
# We're in smart spawning mode.
|
||||||
|
$redis = DiscourseRedis.new
|
||||||
|
Discourse::Application.config.cache_store.reconnect
|
||||||
|
else
|
||||||
|
# We're in conservative spawning mode. We don't need to do anything.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
14
config/initializers/airbrake.rb.sample
Normal file
14
config/initializers/airbrake.rb.sample
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Internally Dicourse uses Errbit for error logging,
|
||||||
|
# you can to by setting up an instance and amending this file
|
||||||
|
require 'airbrake'
|
||||||
|
Airbrake.configure do |config|
|
||||||
|
config.api_key = 'YOUR API KEY'
|
||||||
|
config.host = 'errors.example.com'
|
||||||
|
config.port = 80
|
||||||
|
config.secure = config.port == 443
|
||||||
|
|
||||||
|
|
||||||
|
# IP Spoof errors can be ignored
|
||||||
|
config.ignore << "ActionDispatch::RemoteIp::IpSpoofAttackError"
|
||||||
|
end
|
||||||
|
|
@ -1,16 +1,7 @@
|
|||||||
require "#{Rails.root}/lib/discourse_redis"
|
|
||||||
|
|
||||||
$redis = DiscourseRedis.new
|
|
||||||
|
|
||||||
if Rails.env.development? && !ENV['DO_NOT_FLUSH_REDIS']
|
|
||||||
puts "Flushing redis (development mode)"
|
|
||||||
$redis.flushall
|
|
||||||
end
|
|
||||||
|
|
||||||
Sidekiq.configure_server do |config|
|
Sidekiq.configure_server do |config|
|
||||||
config.redis = { :url => $redis.url, :namespace => 'sidekiq' }
|
config.redis = { :url => $redis.url, :namespace => 'sidekiq' }
|
||||||
end
|
end
|
||||||
|
|
||||||
Sidekiq.configure_client do |config|
|
Sidekiq.configure_client do |config|
|
||||||
config.redis = { :url => $redis.url, :namespace => 'sidekiq' }
|
config.redis = { :url => $redis.url, :namespace => 'sidekiq' }
|
||||||
end
|
end
|
||||||
|
@ -24,6 +24,7 @@ cs:
|
|||||||
you: "Vy"
|
you: "Vy"
|
||||||
ok: "ok"
|
ok: "ok"
|
||||||
or: "nebo"
|
or: "nebo"
|
||||||
|
now: "právě teď"
|
||||||
|
|
||||||
suggested_topics:
|
suggested_topics:
|
||||||
title: "Doporučená témata"
|
title: "Doporučená témata"
|
||||||
@ -279,7 +280,6 @@ cs:
|
|||||||
show_preview: 'zobrazit náhled »'
|
show_preview: 'zobrazit náhled »'
|
||||||
hide_preview: '« skrýt náhled'
|
hide_preview: '« skrýt náhled'
|
||||||
|
|
||||||
quote_title: "Citovat příspěvek"
|
|
||||||
bold_title: "Tučně"
|
bold_title: "Tučně"
|
||||||
bold_text: "tučný text"
|
bold_text: "tučný text"
|
||||||
italic_title: "Kurzíva"
|
italic_title: "Kurzíva"
|
||||||
|
@ -24,6 +24,7 @@ en:
|
|||||||
you: "You"
|
you: "You"
|
||||||
ok: "ok"
|
ok: "ok"
|
||||||
or: "or"
|
or: "or"
|
||||||
|
now: "just now"
|
||||||
|
|
||||||
suggested_topics:
|
suggested_topics:
|
||||||
title: "Suggested Topics"
|
title: "Suggested Topics"
|
||||||
@ -91,7 +92,8 @@ en:
|
|||||||
ok: "Your name looks good."
|
ok: "Your name looks good."
|
||||||
username:
|
username:
|
||||||
title: "Username"
|
title: "Username"
|
||||||
instructions: "People can mention you as @{{username}}."
|
instructions: "Must be unique, no spaces. People can mention you as @username."
|
||||||
|
short_instructions: "People can mention you as @{{username}}."
|
||||||
available: "Your username is available."
|
available: "Your username is available."
|
||||||
global_match: "Email matches the registered username."
|
global_match: "Email matches the registered username."
|
||||||
global_mismatch: "Already registered. Try {{suggestion}}?"
|
global_mismatch: "Already registered. Try {{suggestion}}?"
|
||||||
@ -101,6 +103,9 @@ en:
|
|||||||
checking: "Checking username availability..."
|
checking: "Checking username availability..."
|
||||||
enter_email: 'Username found. Enter matching email.'
|
enter_email: 'Username found. Enter matching email.'
|
||||||
|
|
||||||
|
password_confirmation:
|
||||||
|
title: "Password Again"
|
||||||
|
|
||||||
last_posted: "Last Post"
|
last_posted: "Last Post"
|
||||||
last_emailed: "Last Emailed"
|
last_emailed: "Last Emailed"
|
||||||
last_seen: "Last Seen"
|
last_seen: "Last Seen"
|
||||||
@ -279,7 +284,6 @@ en:
|
|||||||
show_preview: 'show preview »'
|
show_preview: 'show preview »'
|
||||||
hide_preview: '« hide preview'
|
hide_preview: '« hide preview'
|
||||||
|
|
||||||
quote_title: "Quote Post"
|
|
||||||
bold_title: "Strong"
|
bold_title: "Strong"
|
||||||
bold_text: "strong text"
|
bold_text: "strong text"
|
||||||
italic_title: "Emphasis"
|
italic_title: "Emphasis"
|
||||||
|
@ -95,7 +95,8 @@ fr:
|
|||||||
ok: "Votre nom à l'air sympa !."
|
ok: "Votre nom à l'air sympa !."
|
||||||
username:
|
username:
|
||||||
title: "Pseudo"
|
title: "Pseudo"
|
||||||
instructions: "Les gens peuvent vous mentionner avec @{{username}}."
|
instructions: "Doit être unique et ne pas contenir d'espace. Les gens pourrons vous mentionner avec @pseudo."
|
||||||
|
short_instructions: "Les gens peuvent vous mentionner avec @{{username}}."
|
||||||
available: "Votre pseudo est disponible."
|
available: "Votre pseudo est disponible."
|
||||||
global_match: "L'adresse email correspond au pseudo enregistré."
|
global_match: "L'adresse email correspond au pseudo enregistré."
|
||||||
global_mismatch: "Déjà enregistré. Essayez {{suggestion}} ?"
|
global_mismatch: "Déjà enregistré. Essayez {{suggestion}} ?"
|
||||||
@ -105,6 +106,9 @@ fr:
|
|||||||
checking: "Vérification de la disponibilité de votre pseudo..."
|
checking: "Vérification de la disponibilité de votre pseudo..."
|
||||||
enter_email: "Pseudo trouvé. Entrez l'adresse email correspondante."
|
enter_email: "Pseudo trouvé. Entrez l'adresse email correspondante."
|
||||||
|
|
||||||
|
password_confirmation:
|
||||||
|
title: "Confirmation"
|
||||||
|
|
||||||
last_posted: "Dernier message"
|
last_posted: "Dernier message"
|
||||||
last_emailed: "Dernier mail"
|
last_emailed: "Dernier mail"
|
||||||
last_seen: "Dernier vu"
|
last_seen: "Dernier vu"
|
||||||
@ -272,7 +276,6 @@ fr:
|
|||||||
create_topic: "Créer une discussion"
|
create_topic: "Créer une discussion"
|
||||||
create_pm: "Créer un message privé."
|
create_pm: "Créer un message privé."
|
||||||
|
|
||||||
quote_title: "Citer un message"
|
|
||||||
bold_title: "Gras"
|
bold_title: "Gras"
|
||||||
bold_text: "texte en gras"
|
bold_text: "texte en gras"
|
||||||
italic_title: "Italique"
|
italic_title: "Italique"
|
||||||
|
@ -285,7 +285,6 @@ zh_CN:
|
|||||||
show_preview: '显示预览 »'
|
show_preview: '显示预览 »'
|
||||||
hide_preview: '« 隐藏预览'
|
hide_preview: '« 隐藏预览'
|
||||||
|
|
||||||
quote_title: "引用帖子"
|
|
||||||
bold_title: "加粗"
|
bold_title: "加粗"
|
||||||
bold_text: "加粗文字"
|
bold_text: "加粗文字"
|
||||||
italic_title: "斜体"
|
italic_title: "斜体"
|
||||||
|
@ -286,8 +286,8 @@ cs:
|
|||||||
new_topics_rollup: "Kolik nových témat může být vloženo do seznamu než budou tato témata shrnuta do číselné hodnoty"
|
new_topics_rollup: "Kolik nových témat může být vloženo do seznamu než budou tato témata shrnuta do číselné hodnoty"
|
||||||
onebox_max_chars: "Maximální počet znaků, které může 'onebox' naimportovat z externího webu"
|
onebox_max_chars: "Maximální počet znaků, které může 'onebox' naimportovat z externího webu"
|
||||||
|
|
||||||
logo_url: "Logo vašeho webu, např. http://xyz.com/x.png"
|
logo_url: "Logo vašeho webu, např. http://example.com/logo.png"
|
||||||
logo_small_url: "Malé logo, které se použije pokud odskrolujete dolů v tématu, např. http://xyz.com/x-small.png"
|
logo_small_url: "Malé logo, které se použije pokud odskrolujete dolů v tématu, např. http://example.com/logo-small.png"
|
||||||
favicon_url: "Favicona vašeho webu, viz http://en.wikipedia.org/wiki/Favicon"
|
favicon_url: "Favicona vašeho webu, viz http://en.wikipedia.org/wiki/Favicon"
|
||||||
notification_email: "Návratová emailová adresa, která se použije u systémových emailů, jako jsou notifikace o zapomenutém heslu, nových účtech, atd."
|
notification_email: "Návratová emailová adresa, která se použije u systémových emailů, jako jsou notifikace o zapomenutém heslu, nových účtech, atd."
|
||||||
use_ssl: "Má být web přístupný přes SSL?"
|
use_ssl: "Má být web přístupný přes SSL?"
|
||||||
|
@ -286,8 +286,8 @@ de:
|
|||||||
new_topics_rollup: "Zahl der Themen, die vor dem Aufrollen der Themenliste hinzugefügt werden."
|
new_topics_rollup: "Zahl der Themen, die vor dem Aufrollen der Themenliste hinzugefügt werden."
|
||||||
onebox_max_chars: "Maximale Zahl der Zeichen, die eine Onebox von einer externen Webseite in einen Beitrag lädt."
|
onebox_max_chars: "Maximale Zahl der Zeichen, die eine Onebox von einer externen Webseite in einen Beitrag lädt."
|
||||||
|
|
||||||
logo_url: "Das Logo Deiner Seite, zum Beispiel: http://xyz.com/x.png"
|
logo_url: "Das Logo Deiner Seite, zum Beispiel: http://example.com/logo.png"
|
||||||
logo_small_url: "Kleines Logo Deiner Seite, das beim Herunterscrollen in einem Thema gezeigt wird, zum Beispiel: http://xyz.com/x-small.png"
|
logo_small_url: "Kleines Logo Deiner Seite, das beim Herunterscrollen in einem Thema gezeigt wird, zum Beispiel: http://example.com/logo-small.png"
|
||||||
favicon_url: "Das Favicon Deiner Seite, siehe http://de.wikipedia.org/wiki/Favicon"
|
favicon_url: "Das Favicon Deiner Seite, siehe http://de.wikipedia.org/wiki/Favicon"
|
||||||
notification_email: "Die Antwortadresse, die in Systemmails (zum Beispiel zur Passwortwiederherstellung, neuen Konten, etc.) eingetragen wird."
|
notification_email: "Die Antwortadresse, die in Systemmails (zum Beispiel zur Passwortwiederherstellung, neuen Konten, etc.) eingetragen wird."
|
||||||
use_ssl: "Soll die Seite via SSL nutzbar sein?"
|
use_ssl: "Soll die Seite via SSL nutzbar sein?"
|
||||||
|
@ -293,8 +293,8 @@ fr:
|
|||||||
category_post_template: "Le modèle de message de définition d'une catégorie utilisé lorsque vous créez une nouvelle catégorie"
|
category_post_template: "Le modèle de message de définition d'une catégorie utilisé lorsque vous créez une nouvelle catégorie"
|
||||||
onebox_max_chars: "Nombre maximal de caractères qu'une boîte peut importer en blob de texte."
|
onebox_max_chars: "Nombre maximal de caractères qu'une boîte peut importer en blob de texte."
|
||||||
|
|
||||||
logo_url: "Le logo de votre site, par exemple: http://xyz.com/x.png"
|
logo_url: "Le logo de votre site, par exemple: http://example.com/logo.png"
|
||||||
logo_small_url: "La version minifiée du logo de votre site (affichée sur les pages de discussions) ex: http://xyz.com/x-min.png"
|
logo_small_url: "La version minifiée du logo de votre site (affichée sur les pages de discussions) ex: http://example.com/logo-small.png"
|
||||||
favicon_url: "Le favicon de votre site"
|
favicon_url: "Le favicon de votre site"
|
||||||
notification_email: "L'adresse email utilisée pour notifier les utilisateurs de mots de passe perdus, d'activation de compte, etc."
|
notification_email: "L'adresse email utilisée pour notifier les utilisateurs de mots de passe perdus, d'activation de compte, etc."
|
||||||
use_ssl: "Le site doit-il être accessible via SSL?"
|
use_ssl: "Le site doit-il être accessible via SSL?"
|
||||||
|
@ -254,8 +254,8 @@ nl:
|
|||||||
post_onebox_maxlength: "Maximale lengte van een 'oneboxed' discourse post."
|
post_onebox_maxlength: "Maximale lengte van een 'oneboxed' discourse post."
|
||||||
new_topics_rollup: "Hoeveel topics er aan een topic-lijst kunnen worden toegevoegd voordat de lijst oprolt."
|
new_topics_rollup: "Hoeveel topics er aan een topic-lijst kunnen worden toegevoegd voordat de lijst oprolt."
|
||||||
onebox_max_chars: "Het maximaal aantal karakters dat een 'onebox' zal importeren in een lap tekst."
|
onebox_max_chars: "Het maximaal aantal karakters dat een 'onebox' zal importeren in een lap tekst."
|
||||||
logo_url: "Het logo van je site bijv: http://xyz.com/x.png"
|
logo_url: "Het logo van je site bijv: http://example.com/logo.png"
|
||||||
logo_small_url: "Het kleine logo van je site (wordt weergegeven op topic-pagina's) bijv: http://xyz.com/x.png"
|
logo_small_url: "Het kleine logo van je site (wordt weergegeven op topic-pagina's) bijv: http://example.com/logo-small.png"
|
||||||
favicon_url: "Een favicon voor je site"
|
favicon_url: "Een favicon voor je site"
|
||||||
notification_email: "Het email-adres waarmee gebruikers op de hoogte worden gesteld van verloren wachtwoorden, nieuwe accounts etc."
|
notification_email: "Het email-adres waarmee gebruikers op de hoogte worden gesteld van verloren wachtwoorden, nieuwe accounts etc."
|
||||||
use_ssl: "Moet de site toegankelijk zijn via SSL?"
|
use_ssl: "Moet de site toegankelijk zijn via SSL?"
|
||||||
|
@ -257,8 +257,8 @@ pt:
|
|||||||
category_post_template: "O template para um post que aparece quando crias uma categoria."
|
category_post_template: "O template para um post que aparece quando crias uma categoria."
|
||||||
new_topics_rollup: "Quantos tópicos podem ser inseridos na lista de tópicos antes de serem puxados."
|
new_topics_rollup: "Quantos tópicos podem ser inseridos na lista de tópicos antes de serem puxados."
|
||||||
onebox_max_chars: "Máximo número de caracteres que um onebox vai importar num único pedaço."
|
onebox_max_chars: "Máximo número de caracteres que um onebox vai importar num único pedaço."
|
||||||
logo_url: "O logo para o teu site eg: http://xyz.com/x.png"
|
logo_url: "O logo para o teu site eg: http://example.com/logo.png"
|
||||||
logo_small_url: "O logo em pequeno para o teu site (aparece nas páginas dos tópicos) eg: http://xyz.com/x.png"
|
logo_small_url: "O logo em pequeno para o teu site (aparece nas páginas dos tópicos) eg: http://example.com/logo-small.png"
|
||||||
favicon_url: "Um favicon para o teu site"
|
favicon_url: "Um favicon para o teu site"
|
||||||
notification_email: "O endereço de email a ser usado para notificar os utilizadores de password esquecida, novas contas, etc."
|
notification_email: "O endereço de email a ser usado para notificar os utilizadores de password esquecida, novas contas, etc."
|
||||||
use_ssl: "Deverá o site estár acessivel via SSL?"
|
use_ssl: "Deverá o site estár acessivel via SSL?"
|
||||||
|
@ -286,8 +286,8 @@ zh_CN:
|
|||||||
new_topics_rollup: "主题列表卷起显示为主题数量前,可以往主题列表中插入多少新主题"
|
new_topics_rollup: "主题列表卷起显示为主题数量前,可以往主题列表中插入多少新主题"
|
||||||
onebox_max_chars: "从外部网站导入到一个单厢帖(Onebox post)的最大字符数"
|
onebox_max_chars: "从外部网站导入到一个单厢帖(Onebox post)的最大字符数"
|
||||||
|
|
||||||
logo_url: "你的站点标志图片,例如:http://xyz.com/x.png"
|
logo_url: "你的站点标志图片,例如:http://example.com/logo.png"
|
||||||
logo_small_url: "你的站点的小号标志图片,例如:http://xyz.com/x-small.png,用于卷起主题列表时显示"
|
logo_small_url: "你的站点的小号标志图片,例如:http://example.com/logo-small.png,用于卷起主题列表时显示"
|
||||||
favicon_url: "你的站点图标(favicon),参考 http://zh.wikipedia.org/wiki/Favicon"
|
favicon_url: "你的站点图标(favicon),参考 http://zh.wikipedia.org/wiki/Favicon"
|
||||||
notification_email: "邮件回复地址,当发送系统邮件,例如通知用户找回密码、新用户注册等等时,所使用的发信人地址"
|
notification_email: "邮件回复地址,当发送系统邮件,例如通知用户找回密码、新用户注册等等时,所使用的发信人地址"
|
||||||
use_ssl: "使用 SSL 安全套接层来访问本站吗?"
|
use_ssl: "使用 SSL 安全套接层来访问本站吗?"
|
||||||
|
9
db/migrate/20130311181327_remove_extra_spam_record.rb
Normal file
9
db/migrate/20130311181327_remove_extra_spam_record.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class RemoveExtraSpamRecord < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
execute "UPDATE post_actions SET post_action_type_id = 7 where post_action_type_id = 8"
|
||||||
|
execute "DELETE FROM post_action_types WHERE id = 8"
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
end
|
||||||
|
end
|
@ -4644,4 +4644,6 @@ INSERT INTO schema_migrations (version) VALUES ('20130221215017');
|
|||||||
|
|
||||||
INSERT INTO schema_migrations (version) VALUES ('20130226015336');
|
INSERT INTO schema_migrations (version) VALUES ('20130226015336');
|
||||||
|
|
||||||
INSERT INTO schema_migrations (version) VALUES ('20130306180148');
|
INSERT INTO schema_migrations (version) VALUES ('20130306180148');
|
||||||
|
|
||||||
|
INSERT INTO schema_migrations (version) VALUES ('20130311181327');
|
@ -3,18 +3,7 @@ module AgeWords
|
|||||||
def self.age_words(secs)
|
def self.age_words(secs)
|
||||||
return "—" if secs.blank?
|
return "—" if secs.blank?
|
||||||
|
|
||||||
mins = (secs / 60.0)
|
return FreedomPatches::Rails4.distance_of_time_in_words(Time.now, Time.now + secs)
|
||||||
hours = (mins / 60.0)
|
|
||||||
days = (hours / 24.0)
|
|
||||||
months = (days / 30.0)
|
|
||||||
years = (months / 12.0)
|
|
||||||
|
|
||||||
return "#{years.floor}y" if years > 1
|
|
||||||
return "#{months.floor}mo" if months > 1
|
|
||||||
return "#{days.floor}d" if days > 1
|
|
||||||
return "#{hours.floor}h" if hours > 1
|
|
||||||
return "< 1m" if mins < 1
|
|
||||||
return "#{mins.floor}m"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -35,6 +35,13 @@ class DiscourseRedis
|
|||||||
RailsMultisite::ConnectionManagement.current_db
|
RailsMultisite::ConnectionManagement.current_db
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.new_redis_store
|
||||||
|
redis_config = YAML::load(File.open("#{Rails.root}/config/redis.yml"))[Rails.env]
|
||||||
|
redis_store = ActiveSupport::Cache::RedisStore.new "redis://#{redis_config['host']}:#{redis_config['port']}/#{redis_config['cache_db']}"
|
||||||
|
redis_store.options[:namespace] = -> { DiscourseRedis.namespace }
|
||||||
|
redis_store
|
||||||
|
end
|
||||||
|
|
||||||
def url
|
def url
|
||||||
"redis://#{@config['host']}:#{@config['port']}/#{@config['db']}"
|
"redis://#{@config['host']}:#{@config['port']}/#{@config['db']}"
|
||||||
end
|
end
|
||||||
|
@ -98,7 +98,7 @@ module Search
|
|||||||
return nil if term.blank?
|
return nil if term.blank?
|
||||||
|
|
||||||
# We are stripping only symbols taking place in FTS and simply sanitizing the rest.
|
# We are stripping only symbols taking place in FTS and simply sanitizing the rest.
|
||||||
sanitized_term = PG::Connection.escape_string(term.gsub(/[:()&!]/,''))
|
sanitized_term = PG::Connection.escape_string(term.gsub(/[:()&!]/,''))
|
||||||
|
|
||||||
# really short terms are totally pointless
|
# really short terms are totally pointless
|
||||||
return nil if sanitized_term.blank? || sanitized_term.length < min_search_term_length
|
return nil if sanitized_term.blank? || sanitized_term.length < min_search_term_length
|
||||||
@ -155,7 +155,11 @@ module Search
|
|||||||
type = row.delete('type')
|
type = row.delete('type')
|
||||||
|
|
||||||
# Add the slug for topics
|
# Add the slug for topics
|
||||||
row['url'].gsub!('slug', Slug.for(row['title'])) if type == 'topic'
|
if type == 'topic'
|
||||||
|
new_slug = Slug.for(row['title'])
|
||||||
|
new_slug = "topic" if new_slug.blank?
|
||||||
|
row['url'].gsub!('slug', new_slug)
|
||||||
|
end
|
||||||
|
|
||||||
# Remove attributes when we know they don't matter
|
# Remove attributes when we know they don't matter
|
||||||
row.delete('id')
|
row.delete('id')
|
||||||
|
@ -192,7 +192,7 @@ class TopicQuery
|
|||||||
if @user_id.present?
|
if @user_id.present?
|
||||||
result = result.order(TopicQuery.order_nocategory_with_pinned_sql)
|
result = result.order(TopicQuery.order_nocategory_with_pinned_sql)
|
||||||
else
|
else
|
||||||
result = result.order(TopicQuery.order_basic_bumped)
|
result = result.order(TopicQuery.order_nocategory_basic_bumped)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf-8' />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
|
|
||||||
<meta name="description" content="Discourse.org github commits widget" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css">
|
|
||||||
|
|
||||||
<title>Discourse.org Latest Commits Widget</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="widget-container">
|
|
||||||
<div class="header">
|
|
||||||
<h1>Latest Changes</h4>
|
|
||||||
</div>
|
|
||||||
<ul class="commits-list"></ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
|
||||||
<script src="javascripts/jquery.timeago.js"></script>
|
|
||||||
<script src="javascripts/commits-widget.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -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 = $('<li></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 ) {
|
|
||||||
$('<div class="left"><img src="https://www.gravatar.com/avatar/' + commit.author.gravatar_id + '.png?s=38&r=pg&d=identicon"></div>').appendTo( $li );
|
|
||||||
} else {
|
|
||||||
$('<div class="left"><img src="https://www.gravatar.com/avatar/b30fff48d257cdd17c4437afac19fd30.png?s=38&r=pg&d=identicon"></div>').appendTo( $li );
|
|
||||||
}
|
|
||||||
$right = $('<div class="right"></div>').appendTo( $li );
|
|
||||||
$('<span class="commit-message"><a href="https://github.com/discourse/discourse/commit/' + commit.sha + '" target="_blank">' + commit.commit.message + '</a></span><br/>').appendTo( $right );
|
|
||||||
$('<span class="commit-meta">by <span class="committer-name">' + commit.commit.author.name + '</span> - <span class="commit-time">' + $.timeago(commit.commit.committer.date) + '</span></span>').appendTo( $right );
|
|
||||||
$('<div class="clearfix"></div>').appendTo( $li );
|
|
||||||
} else {
|
|
||||||
// Render nothing. Or render a message:
|
|
||||||
// $('<div class="left"> </div>').appendTo( $li );
|
|
||||||
// $right = $('<div class="right"></div>').appendTo( $li );
|
|
||||||
// $('<span class="commit-meta">this commit cannot be rendered</span>').appendTo( $right );
|
|
||||||
// $('<div class="clearfix"></div>').appendTo( $li );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
@ -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");
|
|
||||||
}));
|
|
@ -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;
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ describe Search do
|
|||||||
context 'post indexing observer' do
|
context 'post indexing observer' do
|
||||||
before do
|
before do
|
||||||
@category = Fabricate(:category, name: 'america')
|
@category = Fabricate(:category, name: 'america')
|
||||||
@topic = Fabricate(:topic, title: 'sam test topic', category: @category)
|
@topic = Fabricate(:topic, title: 'sam saffron test topic', category: @category)
|
||||||
@post = Fabricate(:post, topic: @topic, raw: 'this <b>fun test</b> <img src="bla" title="my image">')
|
@post = Fabricate(:post, topic: @topic, raw: 'this <b>fun test</b> <img src="bla" title="my image">')
|
||||||
@indexed = Topic.exec_sql("select search_data from posts_search where id = #{@post.id}").first["search_data"]
|
@indexed = Topic.exec_sql("select search_data from posts_search where id = #{@post.id}").first["search_data"]
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Fabricator(:topic) do
|
Fabricator(:topic) do
|
||||||
user
|
user
|
||||||
title { sequence(:title) { |i| "Test topic #{i}" } }
|
title { sequence(:title) { |i| "This is a test topic #{i}" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:deleted_topic, from: :topic) do
|
Fabricator(:deleted_topic, from: :topic) do
|
||||||
@ -12,7 +12,7 @@ end
|
|||||||
|
|
||||||
Fabricator(:private_message_topic, from: :topic) do
|
Fabricator(:private_message_topic, from: :topic) do
|
||||||
user
|
user
|
||||||
title { sequence(:title) { |i| "Private Message #{i}" } }
|
title { sequence(:title) { |i| "This is a private message #{i}" } }
|
||||||
archetype "private_message"
|
archetype "private_message"
|
||||||
topic_allowed_users{|t| [
|
topic_allowed_users{|t| [
|
||||||
Fabricate.build(:topic_allowed_user, user_id: t[:user].id),
|
Fabricate.build(:topic_allowed_user, user_id: t[:user].id),
|
||||||
|
@ -102,10 +102,7 @@ describe Report do
|
|||||||
|
|
||||||
it 'should cache the data set' do
|
it 'should cache the data set' do
|
||||||
$redis.expects(:setex).with do |key, expiry, string|
|
$redis.expects(:setex).with do |key, expiry, string|
|
||||||
key == 'signups:data' and
|
string =~ /(\d)+-(\d)+-(\d)+,1/ and string =~ /(\d)+-(\d)+-(\d)+,2/
|
||||||
expiry == Report.cache_expiry # and
|
|
||||||
string.include? "#{1.days.ago.to_date.to_s},1" and
|
|
||||||
string.include? "#{0.days.ago.to_date.to_s},2"
|
|
||||||
end
|
end
|
||||||
report()
|
report()
|
||||||
end
|
end
|
||||||
|
@ -193,12 +193,12 @@ describe Topic do
|
|||||||
it "enqueues a job to notify users" do
|
it "enqueues a job to notify users" do
|
||||||
topic.stubs(:add_moderator_post)
|
topic.stubs(:add_moderator_post)
|
||||||
Jobs.expects(:enqueue).with(:notify_moved_posts, post_ids: [p1.id, p4.id], moved_by_id: user.id)
|
Jobs.expects(:enqueue).with(:notify_moved_posts, post_ids: [p1.id, p4.id], moved_by_id: user.id)
|
||||||
topic.move_posts(user, "new topic name", [p1.id, p4.id])
|
topic.move_posts(user, "new testing topic name", [p1.id, p4.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "adds a moderator post at the location of the first moved post" do
|
it "adds a moderator post at the location of the first moved post" do
|
||||||
topic.expects(:add_moderator_post).with(user, instance_of(String), has_entries(post_number: 2))
|
topic.expects(:add_moderator_post).with(user, instance_of(String), has_entries(post_number: 2))
|
||||||
topic.move_posts(user, "new topic name", [p2.id, p4.id])
|
topic.move_posts(user, "new testing topic name", [p2.id, p4.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -206,11 +206,11 @@ describe Topic do
|
|||||||
context "errors" do
|
context "errors" do
|
||||||
|
|
||||||
it "raises an error when one of the posts doesn't exist" do
|
it "raises an error when one of the posts doesn't exist" do
|
||||||
lambda { topic.move_posts(user, "new topic name", [1003]) }.should raise_error(Discourse::InvalidParameters)
|
lambda { topic.move_posts(user, "new testing topic name", [1003]) }.should raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises an error if no posts were moved" do
|
it "raises an error if no posts were moved" do
|
||||||
lambda { topic.move_posts(user, "new topic name", []) }.should raise_error(Discourse::InvalidParameters)
|
lambda { topic.move_posts(user, "new testing topic name", []) }.should raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -221,7 +221,7 @@ describe Topic do
|
|||||||
TopicUser.update_last_read(user, topic.id, p4.post_number, 0)
|
TopicUser.update_last_read(user, topic.id, p4.post_number, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:new_topic) { topic.move_posts(user, "new topic name", [p2.id, p4.id]) }
|
let!(:new_topic) { topic.move_posts(user, "new testing topic name", [p2.id, p4.id]) }
|
||||||
|
|
||||||
|
|
||||||
it "updates the user's last_read_post_number" do
|
it "updates the user's last_read_post_number" do
|
||||||
|
@ -9,8 +9,10 @@ class MessageBus::Rack::Middleware
|
|||||||
def self.start_listener
|
def self.start_listener
|
||||||
unless @started_listener
|
unless @started_listener
|
||||||
MessageBus.subscribe do |msg|
|
MessageBus.subscribe do |msg|
|
||||||
EM.next_tick do
|
if EM.reactor_running?
|
||||||
@@connection_manager.notify_clients(msg) if @@connection_manager
|
EM.next_tick do
|
||||||
|
@@connection_manager.notify_clients(msg) if @@connection_manager
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@started_listener = true
|
@started_listener = true
|
||||||
@ -73,7 +75,7 @@ class MessageBus::Rack::Middleware
|
|||||||
|
|
||||||
if backlog.length > 0
|
if backlog.length > 0
|
||||||
[200, headers, [self.class.backlog_to_json(backlog)] ]
|
[200, headers, [self.class.backlog_to_json(backlog)] ]
|
||||||
elsif MessageBus.long_polling_enabled? && env['QUERY_STRING'] !~ /dlp=t/
|
elsif MessageBus.long_polling_enabled? && env['QUERY_STRING'] !~ /dlp=t/ && EM.reactor_running?
|
||||||
response = Thin::AsyncResponse.new(env)
|
response = Thin::AsyncResponse.new(env)
|
||||||
response.headers["Cache-Control"] = "must-revalidate, private, max-age=0"
|
response.headers["Cache-Control"] = "must-revalidate, private, max-age=0"
|
||||||
response.headers["Content-Type"] ="application/json; charset=utf-8"
|
response.headers["Content-Type"] ="application/json; charset=utf-8"
|
||||||
|
Loading…
Reference in New Issue
Block a user